1/*
2 * This file is part of UBIFS.
3 *
4 * Copyright (C) 2006-2008 Nokia Corporation.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * Authors: Adrian Hunter
20 *          Artem Bityutskiy (Битюцкий Артём)
21 */
22
23/*
24 * This file implements the functions that access LEB properties and their
25 * categories. LEBs are categorized based on the needs of UBIFS, and the
26 * categories are stored as either heaps or lists to provide a fast way of
27 * finding a LEB in a particular category. For example, UBIFS may need to find
28 * an empty LEB for the journal, or a very dirty LEB for garbage collection.
29 */
30
31#include "ubifs.h"
32
33/**
34 * get_heap_comp_val - get the LEB properties value for heap comparisons.
35 * @lprops: LEB properties
36 * @cat: LEB category
37 */
38static int get_heap_comp_val(struct ubifs_lprops *lprops, int cat)
39{
40	switch (cat) {
41	case LPROPS_FREE:
42		return lprops->free;
43	case LPROPS_DIRTY_IDX:
44		return lprops->free + lprops->dirty;
45	default:
46		return lprops->dirty;
47	}
48}
49
50/**
51 * move_up_lpt_heap - move a new heap entry up as far as possible.
52 * @c: UBIFS file-system description object
53 * @heap: LEB category heap
54 * @lprops: LEB properties to move
55 * @cat: LEB category
56 *
57 * New entries to a heap are added at the bottom and then moved up until the
58 * parent's value is greater.  In the case of LPT's category heaps, the value
59 * is either the amount of free space or the amount of dirty space, depending
60 * on the category.
61 */
62static void move_up_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
63			     struct ubifs_lprops *lprops, int cat)
64{
65	int val1, val2, hpos;
66
67	hpos = lprops->hpos;
68	if (!hpos)
69		return; /* Already top of the heap */
70	val1 = get_heap_comp_val(lprops, cat);
71	/* Compare to parent and, if greater, move up the heap */
72	do {
73		int ppos = (hpos - 1) / 2;
74
75		val2 = get_heap_comp_val(heap->arr[ppos], cat);
76		if (val2 >= val1)
77			return;
78		/* Greater than parent so move up */
79		heap->arr[ppos]->hpos = hpos;
80		heap->arr[hpos] = heap->arr[ppos];
81		heap->arr[ppos] = lprops;
82		lprops->hpos = ppos;
83		hpos = ppos;
84	} while (hpos);
85}
86
87/**
88 * adjust_lpt_heap - move a changed heap entry up or down the heap.
89 * @c: UBIFS file-system description object
90 * @heap: LEB category heap
91 * @lprops: LEB properties to move
92 * @hpos: heap position of @lprops
93 * @cat: LEB category
94 *
95 * Changed entries in a heap are moved up or down until the parent's value is
96 * greater.  In the case of LPT's category heaps, the value is either the amount
97 * of free space or the amount of dirty space, depending on the category.
98 */
99static void adjust_lpt_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap,
100			    struct ubifs_lprops *lprops, int hpos, int cat)
101{
102	int val1, val2, val3, cpos;
103
104	val1 = get_heap_comp_val(lprops, cat);
105	/* Compare to parent and, if greater than parent, move up the heap */
106	if (hpos) {
107		int ppos = (hpos - 1) / 2;
108
109		val2 = get_heap_comp_val(heap->arr[ppos], cat);
110		if (val1 > val2) {
111			/* Greater than parent so move up */
112			while (1) {
113				heap->arr[ppos]->hpos = hpos;
114				heap->arr[hpos] = heap->arr[ppos];
115				heap->arr[ppos] = lprops;
116				lprops->hpos = ppos;
117				hpos = ppos;
118				if (!hpos)
119					return;
120				ppos = (hpos - 1) / 2;
121				val2 = get_heap_comp_val(heap->arr[ppos], cat);
122				if (val1 <= val2)
123					return;
124				/* Still greater than parent so keep going */
125			}
126		}
127	}
128
129	/* Not greater than parent, so compare to children */
130	while (1) {
131		/* Compare to left child */
132		cpos = hpos * 2 + 1;
133		if (cpos >= heap->cnt)
134			return;
135		val2 = get_heap_comp_val(heap->arr[cpos], cat);
136		if (val1 < val2) {
137			/* Less than left child, so promote biggest child */
138			if (cpos + 1 < heap->cnt) {
139				val3 = get_heap_comp_val(heap->arr[cpos + 1],
140							 cat);
141				if (val3 > val2)
142					cpos += 1; /* Right child is bigger */
143			}
144			heap->arr[cpos]->hpos = hpos;
145			heap->arr[hpos] = heap->arr[cpos];
146			heap->arr[cpos] = lprops;
147			lprops->hpos = cpos;
148			hpos = cpos;
149			continue;
150		}
151		/* Compare to right child */
152		cpos += 1;
153		if (cpos >= heap->cnt)
154			return;
155		val3 = get_heap_comp_val(heap->arr[cpos], cat);
156		if (val1 < val3) {
157			/* Less than right child, so promote right child */
158			heap->arr[cpos]->hpos = hpos;
159			heap->arr[hpos] = heap->arr[cpos];
160			heap->arr[cpos] = lprops;
161			lprops->hpos = cpos;
162			hpos = cpos;
163			continue;
164		}
165		return;
166	}
167}
168
169/**
170 * add_to_lpt_heap - add LEB properties to a LEB category heap.
171 * @c: UBIFS file-system description object
172 * @lprops: LEB properties to add
173 * @cat: LEB category
174 *
175 * This function returns %1 if @lprops is added to the heap for LEB category
176 * @cat, otherwise %0 is returned because the heap is full.
177 */
178static int add_to_lpt_heap(struct ubifs_info *c, struct ubifs_lprops *lprops,
179			   int cat)
180{
181	struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
182
183	if (heap->cnt >= heap->max_cnt) {
184		const int b = LPT_HEAP_SZ / 2 - 1;
185		int cpos, val1, val2;
186
187		/* Compare to some other LEB on the bottom of heap */
188		/* Pick a position kind of randomly */
189		cpos = (((size_t)lprops >> 4) & b) + b;
190		ubifs_assert(cpos >= b);
191		ubifs_assert(cpos < LPT_HEAP_SZ);
192		ubifs_assert(cpos < heap->cnt);
193
194		val1 = get_heap_comp_val(lprops, cat);
195		val2 = get_heap_comp_val(heap->arr[cpos], cat);
196		if (val1 > val2) {
197			struct ubifs_lprops *lp;
198
199			lp = heap->arr[cpos];
200			lp->flags &= ~LPROPS_CAT_MASK;
201			lp->flags |= LPROPS_UNCAT;
202			list_add(&lp->list, &c->uncat_list);
203			lprops->hpos = cpos;
204			heap->arr[cpos] = lprops;
205			move_up_lpt_heap(c, heap, lprops, cat);
206			dbg_check_heap(c, heap, cat, lprops->hpos);
207			return 1; /* Added to heap */
208		}
209		dbg_check_heap(c, heap, cat, -1);
210		return 0; /* Not added to heap */
211	} else {
212		lprops->hpos = heap->cnt++;
213		heap->arr[lprops->hpos] = lprops;
214		move_up_lpt_heap(c, heap, lprops, cat);
215		dbg_check_heap(c, heap, cat, lprops->hpos);
216		return 1; /* Added to heap */
217	}
218}
219
220/**
221 * remove_from_lpt_heap - remove LEB properties from a LEB category heap.
222 * @c: UBIFS file-system description object
223 * @lprops: LEB properties to remove
224 * @cat: LEB category
225 */
226static void remove_from_lpt_heap(struct ubifs_info *c,
227				 struct ubifs_lprops *lprops, int cat)
228{
229	struct ubifs_lpt_heap *heap;
230	int hpos = lprops->hpos;
231
232	heap = &c->lpt_heap[cat - 1];
233	ubifs_assert(hpos >= 0 && hpos < heap->cnt);
234	ubifs_assert(heap->arr[hpos] == lprops);
235	heap->cnt -= 1;
236	if (hpos < heap->cnt) {
237		heap->arr[hpos] = heap->arr[heap->cnt];
238		heap->arr[hpos]->hpos = hpos;
239		adjust_lpt_heap(c, heap, heap->arr[hpos], hpos, cat);
240	}
241	dbg_check_heap(c, heap, cat, -1);
242}
243
244/**
245 * lpt_heap_replace - replace lprops in a category heap.
246 * @c: UBIFS file-system description object
247 * @old_lprops: LEB properties to replace
248 * @new_lprops: LEB properties with which to replace
249 * @cat: LEB category
250 *
251 * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
252 * and the lprops that the pnode contains.  When that happens, references in
253 * the category heaps to those lprops must be updated to point to the new
254 * lprops.  This function does that.
255 */
256static void lpt_heap_replace(struct ubifs_info *c,
257			     struct ubifs_lprops *old_lprops,
258			     struct ubifs_lprops *new_lprops, int cat)
259{
260	struct ubifs_lpt_heap *heap;
261	int hpos = new_lprops->hpos;
262
263	heap = &c->lpt_heap[cat - 1];
264	heap->arr[hpos] = new_lprops;
265}
266
267/**
268 * ubifs_add_to_cat - add LEB properties to a category list or heap.
269 * @c: UBIFS file-system description object
270 * @lprops: LEB properties to add
271 * @cat: LEB category to which to add
272 *
273 * LEB properties are categorized to enable fast find operations.
274 */
275void ubifs_add_to_cat(struct ubifs_info *c, struct ubifs_lprops *lprops,
276		      int cat)
277{
278	switch (cat) {
279	case LPROPS_DIRTY:
280	case LPROPS_DIRTY_IDX:
281	case LPROPS_FREE:
282		if (add_to_lpt_heap(c, lprops, cat))
283			break;
284		/* No more room on heap so make it un-categorized */
285		cat = LPROPS_UNCAT;
286		/* Fall through */
287	case LPROPS_UNCAT:
288		list_add(&lprops->list, &c->uncat_list);
289		break;
290	case LPROPS_EMPTY:
291		list_add(&lprops->list, &c->empty_list);
292		break;
293	case LPROPS_FREEABLE:
294		list_add(&lprops->list, &c->freeable_list);
295		c->freeable_cnt += 1;
296		break;
297	case LPROPS_FRDI_IDX:
298		list_add(&lprops->list, &c->frdi_idx_list);
299		break;
300	default:
301		ubifs_assert(0);
302	}
303
304	lprops->flags &= ~LPROPS_CAT_MASK;
305	lprops->flags |= cat;
306	c->in_a_category_cnt += 1;
307	ubifs_assert(c->in_a_category_cnt <= c->main_lebs);
308}
309
310/**
311 * ubifs_remove_from_cat - remove LEB properties from a category list or heap.
312 * @c: UBIFS file-system description object
313 * @lprops: LEB properties to remove
314 * @cat: LEB category from which to remove
315 *
316 * LEB properties are categorized to enable fast find operations.
317 */
318static void ubifs_remove_from_cat(struct ubifs_info *c,
319				  struct ubifs_lprops *lprops, int cat)
320{
321	switch (cat) {
322	case LPROPS_DIRTY:
323	case LPROPS_DIRTY_IDX:
324	case LPROPS_FREE:
325		remove_from_lpt_heap(c, lprops, cat);
326		break;
327	case LPROPS_FREEABLE:
328		c->freeable_cnt -= 1;
329		ubifs_assert(c->freeable_cnt >= 0);
330		/* Fall through */
331	case LPROPS_UNCAT:
332	case LPROPS_EMPTY:
333	case LPROPS_FRDI_IDX:
334		ubifs_assert(!list_empty(&lprops->list));
335		list_del(&lprops->list);
336		break;
337	default:
338		ubifs_assert(0);
339	}
340
341	c->in_a_category_cnt -= 1;
342	ubifs_assert(c->in_a_category_cnt >= 0);
343}
344
345/**
346 * ubifs_replace_cat - replace lprops in a category list or heap.
347 * @c: UBIFS file-system description object
348 * @old_lprops: LEB properties to replace
349 * @new_lprops: LEB properties with which to replace
350 *
351 * During commit it is sometimes necessary to copy a pnode (see dirty_cow_pnode)
352 * and the lprops that the pnode contains. When that happens, references in
353 * category lists and heaps must be replaced. This function does that.
354 */
355void ubifs_replace_cat(struct ubifs_info *c, struct ubifs_lprops *old_lprops,
356		       struct ubifs_lprops *new_lprops)
357{
358	int cat;
359
360	cat = new_lprops->flags & LPROPS_CAT_MASK;
361	switch (cat) {
362	case LPROPS_DIRTY:
363	case LPROPS_DIRTY_IDX:
364	case LPROPS_FREE:
365		lpt_heap_replace(c, old_lprops, new_lprops, cat);
366		break;
367	case LPROPS_UNCAT:
368	case LPROPS_EMPTY:
369	case LPROPS_FREEABLE:
370	case LPROPS_FRDI_IDX:
371		list_replace(&old_lprops->list, &new_lprops->list);
372		break;
373	default:
374		ubifs_assert(0);
375	}
376}
377
378/**
379 * ubifs_ensure_cat - ensure LEB properties are categorized.
380 * @c: UBIFS file-system description object
381 * @lprops: LEB properties
382 *
383 * A LEB may have fallen off of the bottom of a heap, and ended up as
384 * un-categorized even though it has enough space for us now. If that is the
385 * case this function will put the LEB back onto a heap.
386 */
387void ubifs_ensure_cat(struct ubifs_info *c, struct ubifs_lprops *lprops)
388{
389	int cat = lprops->flags & LPROPS_CAT_MASK;
390
391	if (cat != LPROPS_UNCAT)
392		return;
393	cat = ubifs_categorize_lprops(c, lprops);
394	if (cat == LPROPS_UNCAT)
395		return;
396	ubifs_remove_from_cat(c, lprops, LPROPS_UNCAT);
397	ubifs_add_to_cat(c, lprops, cat);
398}
399
400/**
401 * ubifs_categorize_lprops - categorize LEB properties.
402 * @c: UBIFS file-system description object
403 * @lprops: LEB properties to categorize
404 *
405 * LEB properties are categorized to enable fast find operations. This function
406 * returns the LEB category to which the LEB properties belong. Note however
407 * that if the LEB category is stored as a heap and the heap is full, the
408 * LEB properties may have their category changed to %LPROPS_UNCAT.
409 */
410int ubifs_categorize_lprops(const struct ubifs_info *c,
411			    const struct ubifs_lprops *lprops)
412{
413	if (lprops->flags & LPROPS_TAKEN)
414		return LPROPS_UNCAT;
415
416	if (lprops->free == c->leb_size) {
417		ubifs_assert(!(lprops->flags & LPROPS_INDEX));
418		return LPROPS_EMPTY;
419	}
420
421	if (lprops->free + lprops->dirty == c->leb_size) {
422		if (lprops->flags & LPROPS_INDEX)
423			return LPROPS_FRDI_IDX;
424		else
425			return LPROPS_FREEABLE;
426	}
427
428	if (lprops->flags & LPROPS_INDEX) {
429		if (lprops->dirty + lprops->free >= c->min_idx_node_sz)
430			return LPROPS_DIRTY_IDX;
431	} else {
432		if (lprops->dirty >= c->dead_wm &&
433		    lprops->dirty > lprops->free)
434			return LPROPS_DIRTY;
435		if (lprops->free > 0)
436			return LPROPS_FREE;
437	}
438
439	return LPROPS_UNCAT;
440}
441
442/**
443 * change_category - change LEB properties category.
444 * @c: UBIFS file-system description object
445 * @lprops: LEB properties to re-categorize
446 *
447 * LEB properties are categorized to enable fast find operations. When the LEB
448 * properties change they must be re-categorized.
449 */
450static void change_category(struct ubifs_info *c, struct ubifs_lprops *lprops)
451{
452	int old_cat = lprops->flags & LPROPS_CAT_MASK;
453	int new_cat = ubifs_categorize_lprops(c, lprops);
454
455	if (old_cat == new_cat) {
456		struct ubifs_lpt_heap *heap;
457
458		/* lprops on a heap now must be moved up or down */
459		if (new_cat < 1 || new_cat > LPROPS_HEAP_CNT)
460			return; /* Not on a heap */
461		heap = &c->lpt_heap[new_cat - 1];
462		adjust_lpt_heap(c, heap, lprops, lprops->hpos, new_cat);
463	} else {
464		ubifs_remove_from_cat(c, lprops, old_cat);
465		ubifs_add_to_cat(c, lprops, new_cat);
466	}
467}
468
469/**
470 * ubifs_calc_dark - calculate LEB dark space size.
471 * @c: the UBIFS file-system description object
472 * @spc: amount of free and dirty space in the LEB
473 *
474 * This function calculates and returns amount of dark space in an LEB which
475 * has @spc bytes of free and dirty space.
476 *
477 * UBIFS is trying to account the space which might not be usable, and this
478 * space is called "dark space". For example, if an LEB has only %512 free
479 * bytes, it is dark space, because it cannot fit a large data node.
480 */
481int ubifs_calc_dark(const struct ubifs_info *c, int spc)
482{
483	ubifs_assert(!(spc & 7));
484
485	if (spc < c->dark_wm)
486		return spc;
487
488	/*
489	 * If we have slightly more space then the dark space watermark, we can
490	 * anyway safely assume it we'll be able to write a node of the
491	 * smallest size there.
492	 */
493	if (spc - c->dark_wm < MIN_WRITE_SZ)
494		return spc - MIN_WRITE_SZ;
495
496	return c->dark_wm;
497}
498
499/**
500 * is_lprops_dirty - determine if LEB properties are dirty.
501 * @c: the UBIFS file-system description object
502 * @lprops: LEB properties to test
503 */
504static int is_lprops_dirty(struct ubifs_info *c, struct ubifs_lprops *lprops)
505{
506	struct ubifs_pnode *pnode;
507	int pos;
508
509	pos = (lprops->lnum - c->main_first) & (UBIFS_LPT_FANOUT - 1);
510	pnode = (struct ubifs_pnode *)container_of(lprops - pos,
511						   struct ubifs_pnode,
512						   lprops[0]);
513	return !test_bit(COW_CNODE, &pnode->flags) &&
514	       test_bit(DIRTY_CNODE, &pnode->flags);
515}
516
517/**
518 * ubifs_change_lp - change LEB properties.
519 * @c: the UBIFS file-system description object
520 * @lp: LEB properties to change
521 * @free: new free space amount
522 * @dirty: new dirty space amount
523 * @flags: new flags
524 * @idx_gc_cnt: change to the count of @idx_gc list
525 *
526 * This function changes LEB properties (@free, @dirty or @flag). However, the
527 * property which has the %LPROPS_NC value is not changed. Returns a pointer to
528 * the updated LEB properties on success and a negative error code on failure.
529 *
530 * Note, the LEB properties may have had to be copied (due to COW) and
531 * consequently the pointer returned may not be the same as the pointer
532 * passed.
533 */
534const struct ubifs_lprops *ubifs_change_lp(struct ubifs_info *c,
535					   const struct ubifs_lprops *lp,
536					   int free, int dirty, int flags,
537					   int idx_gc_cnt)
538{
539	/*
540	 * This is the only function that is allowed to change lprops, so we
541	 * discard the "const" qualifier.
542	 */
543	struct ubifs_lprops *lprops = (struct ubifs_lprops *)lp;
544
545	dbg_lp("LEB %d, free %d, dirty %d, flags %d",
546	       lprops->lnum, free, dirty, flags);
547
548	ubifs_assert(mutex_is_locked(&c->lp_mutex));
549	ubifs_assert(c->lst.empty_lebs >= 0 &&
550		     c->lst.empty_lebs <= c->main_lebs);
551	ubifs_assert(c->freeable_cnt >= 0);
552	ubifs_assert(c->freeable_cnt <= c->main_lebs);
553	ubifs_assert(c->lst.taken_empty_lebs >= 0);
554	ubifs_assert(c->lst.taken_empty_lebs <= c->lst.empty_lebs);
555	ubifs_assert(!(c->lst.total_free & 7) && !(c->lst.total_dirty & 7));
556	ubifs_assert(!(c->lst.total_dead & 7) && !(c->lst.total_dark & 7));
557	ubifs_assert(!(c->lst.total_used & 7));
558	ubifs_assert(free == LPROPS_NC || free >= 0);
559	ubifs_assert(dirty == LPROPS_NC || dirty >= 0);
560
561	if (!is_lprops_dirty(c, lprops)) {
562		lprops = ubifs_lpt_lookup_dirty(c, lprops->lnum);
563		if (IS_ERR(lprops))
564			return lprops;
565	} else
566		ubifs_assert(lprops == ubifs_lpt_lookup_dirty(c, lprops->lnum));
567
568	ubifs_assert(!(lprops->free & 7) && !(lprops->dirty & 7));
569
570	spin_lock(&c->space_lock);
571	if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
572		c->lst.taken_empty_lebs -= 1;
573
574	if (!(lprops->flags & LPROPS_INDEX)) {
575		int old_spc;
576
577		old_spc = lprops->free + lprops->dirty;
578		if (old_spc < c->dead_wm)
579			c->lst.total_dead -= old_spc;
580		else
581			c->lst.total_dark -= ubifs_calc_dark(c, old_spc);
582
583		c->lst.total_used -= c->leb_size - old_spc;
584	}
585
586	if (free != LPROPS_NC) {
587		free = ALIGN(free, 8);
588		c->lst.total_free += free - lprops->free;
589
590		/* Increase or decrease empty LEBs counter if needed */
591		if (free == c->leb_size) {
592			if (lprops->free != c->leb_size)
593				c->lst.empty_lebs += 1;
594		} else if (lprops->free == c->leb_size)
595			c->lst.empty_lebs -= 1;
596		lprops->free = free;
597	}
598
599	if (dirty != LPROPS_NC) {
600		dirty = ALIGN(dirty, 8);
601		c->lst.total_dirty += dirty - lprops->dirty;
602		lprops->dirty = dirty;
603	}
604
605	if (flags != LPROPS_NC) {
606		/* Take care about indexing LEBs counter if needed */
607		if ((lprops->flags & LPROPS_INDEX)) {
608			if (!(flags & LPROPS_INDEX))
609				c->lst.idx_lebs -= 1;
610		} else if (flags & LPROPS_INDEX)
611			c->lst.idx_lebs += 1;
612		lprops->flags = flags;
613	}
614
615	if (!(lprops->flags & LPROPS_INDEX)) {
616		int new_spc;
617
618		new_spc = lprops->free + lprops->dirty;
619		if (new_spc < c->dead_wm)
620			c->lst.total_dead += new_spc;
621		else
622			c->lst.total_dark += ubifs_calc_dark(c, new_spc);
623
624		c->lst.total_used += c->leb_size - new_spc;
625	}
626
627	if ((lprops->flags & LPROPS_TAKEN) && lprops->free == c->leb_size)
628		c->lst.taken_empty_lebs += 1;
629
630	change_category(c, lprops);
631	c->idx_gc_cnt += idx_gc_cnt;
632	spin_unlock(&c->space_lock);
633	return lprops;
634}
635
636/**
637 * ubifs_get_lp_stats - get lprops statistics.
638 * @c: UBIFS file-system description object
639 * @st: return statistics
640 */
641void ubifs_get_lp_stats(struct ubifs_info *c, struct ubifs_lp_stats *lst)
642{
643	spin_lock(&c->space_lock);
644	memcpy(lst, &c->lst, sizeof(struct ubifs_lp_stats));
645	spin_unlock(&c->space_lock);
646}
647
648/**
649 * ubifs_change_one_lp - change LEB properties.
650 * @c: the UBIFS file-system description object
651 * @lnum: LEB to change properties for
652 * @free: amount of free space
653 * @dirty: amount of dirty space
654 * @flags_set: flags to set
655 * @flags_clean: flags to clean
656 * @idx_gc_cnt: change to the count of idx_gc list
657 *
658 * This function changes properties of LEB @lnum. It is a helper wrapper over
659 * 'ubifs_change_lp()' which hides lprops get/release. The arguments are the
660 * same as in case of 'ubifs_change_lp()'. Returns zero in case of success and
661 * a negative error code in case of failure.
662 */
663int ubifs_change_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
664			int flags_set, int flags_clean, int idx_gc_cnt)
665{
666	int err = 0, flags;
667	const struct ubifs_lprops *lp;
668
669	ubifs_get_lprops(c);
670
671	lp = ubifs_lpt_lookup_dirty(c, lnum);
672	if (IS_ERR(lp)) {
673		err = PTR_ERR(lp);
674		goto out;
675	}
676
677	flags = (lp->flags | flags_set) & ~flags_clean;
678	lp = ubifs_change_lp(c, lp, free, dirty, flags, idx_gc_cnt);
679	if (IS_ERR(lp))
680		err = PTR_ERR(lp);
681
682out:
683	ubifs_release_lprops(c);
684	if (err)
685		ubifs_err(c, "cannot change properties of LEB %d, error %d",
686			  lnum, err);
687	return err;
688}
689
690/**
691 * ubifs_update_one_lp - update LEB properties.
692 * @c: the UBIFS file-system description object
693 * @lnum: LEB to change properties for
694 * @free: amount of free space
695 * @dirty: amount of dirty space to add
696 * @flags_set: flags to set
697 * @flags_clean: flags to clean
698 *
699 * This function is the same as 'ubifs_change_one_lp()' but @dirty is added to
700 * current dirty space, not substitutes it.
701 */
702int ubifs_update_one_lp(struct ubifs_info *c, int lnum, int free, int dirty,
703			int flags_set, int flags_clean)
704{
705	int err = 0, flags;
706	const struct ubifs_lprops *lp;
707
708	ubifs_get_lprops(c);
709
710	lp = ubifs_lpt_lookup_dirty(c, lnum);
711	if (IS_ERR(lp)) {
712		err = PTR_ERR(lp);
713		goto out;
714	}
715
716	flags = (lp->flags | flags_set) & ~flags_clean;
717	lp = ubifs_change_lp(c, lp, free, lp->dirty + dirty, flags, 0);
718	if (IS_ERR(lp))
719		err = PTR_ERR(lp);
720
721out:
722	ubifs_release_lprops(c);
723	if (err)
724		ubifs_err(c, "cannot update properties of LEB %d, error %d",
725			  lnum, err);
726	return err;
727}
728
729/**
730 * ubifs_read_one_lp - read LEB properties.
731 * @c: the UBIFS file-system description object
732 * @lnum: LEB to read properties for
733 * @lp: where to store read properties
734 *
735 * This helper function reads properties of a LEB @lnum and stores them in @lp.
736 * Returns zero in case of success and a negative error code in case of
737 * failure.
738 */
739int ubifs_read_one_lp(struct ubifs_info *c, int lnum, struct ubifs_lprops *lp)
740{
741	int err = 0;
742	const struct ubifs_lprops *lpp;
743
744	ubifs_get_lprops(c);
745
746	lpp = ubifs_lpt_lookup(c, lnum);
747	if (IS_ERR(lpp)) {
748		err = PTR_ERR(lpp);
749		ubifs_err(c, "cannot read properties of LEB %d, error %d",
750			  lnum, err);
751		goto out;
752	}
753
754	memcpy(lp, lpp, sizeof(struct ubifs_lprops));
755
756out:
757	ubifs_release_lprops(c);
758	return err;
759}
760
761/**
762 * ubifs_fast_find_free - try to find a LEB with free space quickly.
763 * @c: the UBIFS file-system description object
764 *
765 * This function returns LEB properties for a LEB with free space or %NULL if
766 * the function is unable to find a LEB quickly.
767 */
768const struct ubifs_lprops *ubifs_fast_find_free(struct ubifs_info *c)
769{
770	struct ubifs_lprops *lprops;
771	struct ubifs_lpt_heap *heap;
772
773	ubifs_assert(mutex_is_locked(&c->lp_mutex));
774
775	heap = &c->lpt_heap[LPROPS_FREE - 1];
776	if (heap->cnt == 0)
777		return NULL;
778
779	lprops = heap->arr[0];
780	ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
781	ubifs_assert(!(lprops->flags & LPROPS_INDEX));
782	return lprops;
783}
784
785/**
786 * ubifs_fast_find_empty - try to find an empty LEB quickly.
787 * @c: the UBIFS file-system description object
788 *
789 * This function returns LEB properties for an empty LEB or %NULL if the
790 * function is unable to find an empty LEB quickly.
791 */
792const struct ubifs_lprops *ubifs_fast_find_empty(struct ubifs_info *c)
793{
794	struct ubifs_lprops *lprops;
795
796	ubifs_assert(mutex_is_locked(&c->lp_mutex));
797
798	if (list_empty(&c->empty_list))
799		return NULL;
800
801	lprops = list_entry(c->empty_list.next, struct ubifs_lprops, list);
802	ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
803	ubifs_assert(!(lprops->flags & LPROPS_INDEX));
804	ubifs_assert(lprops->free == c->leb_size);
805	return lprops;
806}
807
808/**
809 * ubifs_fast_find_freeable - try to find a freeable LEB quickly.
810 * @c: the UBIFS file-system description object
811 *
812 * This function returns LEB properties for a freeable LEB or %NULL if the
813 * function is unable to find a freeable LEB quickly.
814 */
815const struct ubifs_lprops *ubifs_fast_find_freeable(struct ubifs_info *c)
816{
817	struct ubifs_lprops *lprops;
818
819	ubifs_assert(mutex_is_locked(&c->lp_mutex));
820
821	if (list_empty(&c->freeable_list))
822		return NULL;
823
824	lprops = list_entry(c->freeable_list.next, struct ubifs_lprops, list);
825	ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
826	ubifs_assert(!(lprops->flags & LPROPS_INDEX));
827	ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
828	ubifs_assert(c->freeable_cnt > 0);
829	return lprops;
830}
831
832/**
833 * ubifs_fast_find_frdi_idx - try to find a freeable index LEB quickly.
834 * @c: the UBIFS file-system description object
835 *
836 * This function returns LEB properties for a freeable index LEB or %NULL if the
837 * function is unable to find a freeable index LEB quickly.
838 */
839const struct ubifs_lprops *ubifs_fast_find_frdi_idx(struct ubifs_info *c)
840{
841	struct ubifs_lprops *lprops;
842
843	ubifs_assert(mutex_is_locked(&c->lp_mutex));
844
845	if (list_empty(&c->frdi_idx_list))
846		return NULL;
847
848	lprops = list_entry(c->frdi_idx_list.next, struct ubifs_lprops, list);
849	ubifs_assert(!(lprops->flags & LPROPS_TAKEN));
850	ubifs_assert((lprops->flags & LPROPS_INDEX));
851	ubifs_assert(lprops->free + lprops->dirty == c->leb_size);
852	return lprops;
853}
854
855/*
856 * Everything below is related to debugging.
857 */
858
859/**
860 * dbg_check_cats - check category heaps and lists.
861 * @c: UBIFS file-system description object
862 *
863 * This function returns %0 on success and a negative error code on failure.
864 */
865int dbg_check_cats(struct ubifs_info *c)
866{
867	struct ubifs_lprops *lprops;
868	struct list_head *pos;
869	int i, cat;
870
871	if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
872		return 0;
873
874	list_for_each_entry(lprops, &c->empty_list, list) {
875		if (lprops->free != c->leb_size) {
876			ubifs_err(c, "non-empty LEB %d on empty list (free %d dirty %d flags %d)",
877				  lprops->lnum, lprops->free, lprops->dirty,
878				  lprops->flags);
879			return -EINVAL;
880		}
881		if (lprops->flags & LPROPS_TAKEN) {
882			ubifs_err(c, "taken LEB %d on empty list (free %d dirty %d flags %d)",
883				  lprops->lnum, lprops->free, lprops->dirty,
884				  lprops->flags);
885			return -EINVAL;
886		}
887	}
888
889	i = 0;
890	list_for_each_entry(lprops, &c->freeable_list, list) {
891		if (lprops->free + lprops->dirty != c->leb_size) {
892			ubifs_err(c, "non-freeable LEB %d on freeable list (free %d dirty %d flags %d)",
893				  lprops->lnum, lprops->free, lprops->dirty,
894				  lprops->flags);
895			return -EINVAL;
896		}
897		if (lprops->flags & LPROPS_TAKEN) {
898			ubifs_err(c, "taken LEB %d on freeable list (free %d dirty %d flags %d)",
899				  lprops->lnum, lprops->free, lprops->dirty,
900				  lprops->flags);
901			return -EINVAL;
902		}
903		i += 1;
904	}
905	if (i != c->freeable_cnt) {
906		ubifs_err(c, "freeable list count %d expected %d", i,
907			  c->freeable_cnt);
908		return -EINVAL;
909	}
910
911	i = 0;
912	list_for_each(pos, &c->idx_gc)
913		i += 1;
914	if (i != c->idx_gc_cnt) {
915		ubifs_err(c, "idx_gc list count %d expected %d", i,
916			  c->idx_gc_cnt);
917		return -EINVAL;
918	}
919
920	list_for_each_entry(lprops, &c->frdi_idx_list, list) {
921		if (lprops->free + lprops->dirty != c->leb_size) {
922			ubifs_err(c, "non-freeable LEB %d on frdi_idx list (free %d dirty %d flags %d)",
923				  lprops->lnum, lprops->free, lprops->dirty,
924				  lprops->flags);
925			return -EINVAL;
926		}
927		if (lprops->flags & LPROPS_TAKEN) {
928			ubifs_err(c, "taken LEB %d on frdi_idx list (free %d dirty %d flags %d)",
929				  lprops->lnum, lprops->free, lprops->dirty,
930				  lprops->flags);
931			return -EINVAL;
932		}
933		if (!(lprops->flags & LPROPS_INDEX)) {
934			ubifs_err(c, "non-index LEB %d on frdi_idx list (free %d dirty %d flags %d)",
935				  lprops->lnum, lprops->free, lprops->dirty,
936				  lprops->flags);
937			return -EINVAL;
938		}
939	}
940
941	for (cat = 1; cat <= LPROPS_HEAP_CNT; cat++) {
942		struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
943
944		for (i = 0; i < heap->cnt; i++) {
945			lprops = heap->arr[i];
946			if (!lprops) {
947				ubifs_err(c, "null ptr in LPT heap cat %d", cat);
948				return -EINVAL;
949			}
950			if (lprops->hpos != i) {
951				ubifs_err(c, "bad ptr in LPT heap cat %d", cat);
952				return -EINVAL;
953			}
954			if (lprops->flags & LPROPS_TAKEN) {
955				ubifs_err(c, "taken LEB in LPT heap cat %d", cat);
956				return -EINVAL;
957			}
958		}
959	}
960
961	return 0;
962}
963
964void dbg_check_heap(struct ubifs_info *c, struct ubifs_lpt_heap *heap, int cat,
965		    int add_pos)
966{
967	int i = 0, j, err = 0;
968
969	if (!dbg_is_chk_gen(c) && !dbg_is_chk_lprops(c))
970		return;
971
972	for (i = 0; i < heap->cnt; i++) {
973		struct ubifs_lprops *lprops = heap->arr[i];
974		struct ubifs_lprops *lp;
975
976		if (i != add_pos)
977			if ((lprops->flags & LPROPS_CAT_MASK) != cat) {
978				err = 1;
979				goto out;
980			}
981		if (lprops->hpos != i) {
982			err = 2;
983			goto out;
984		}
985		lp = ubifs_lpt_lookup(c, lprops->lnum);
986		if (IS_ERR(lp)) {
987			err = 3;
988			goto out;
989		}
990		if (lprops != lp) {
991			ubifs_err(c, "lprops %zx lp %zx lprops->lnum %d lp->lnum %d",
992				  (size_t)lprops, (size_t)lp, lprops->lnum,
993				  lp->lnum);
994			err = 4;
995			goto out;
996		}
997		for (j = 0; j < i; j++) {
998			lp = heap->arr[j];
999			if (lp == lprops) {
1000				err = 5;
1001				goto out;
1002			}
1003			if (lp->lnum == lprops->lnum) {
1004				err = 6;
1005				goto out;
1006			}
1007		}
1008	}
1009out:
1010	if (err) {
1011		ubifs_err(c, "failed cat %d hpos %d err %d", cat, i, err);
1012		dump_stack();
1013		ubifs_dump_heap(c, heap, cat);
1014	}
1015}
1016
1017/**
1018 * scan_check_cb - scan callback.
1019 * @c: the UBIFS file-system description object
1020 * @lp: LEB properties to scan
1021 * @in_tree: whether the LEB properties are in main memory
1022 * @lst: lprops statistics to update
1023 *
1024 * This function returns a code that indicates whether the scan should continue
1025 * (%LPT_SCAN_CONTINUE), whether the LEB properties should be added to the tree
1026 * in main memory (%LPT_SCAN_ADD), or whether the scan should stop
1027 * (%LPT_SCAN_STOP).
1028 */
1029static int scan_check_cb(struct ubifs_info *c,
1030			 const struct ubifs_lprops *lp, int in_tree,
1031			 struct ubifs_lp_stats *lst)
1032{
1033	struct ubifs_scan_leb *sleb;
1034	struct ubifs_scan_node *snod;
1035	int cat, lnum = lp->lnum, is_idx = 0, used = 0, free, dirty, ret;
1036	void *buf = NULL;
1037
1038	cat = lp->flags & LPROPS_CAT_MASK;
1039	if (cat != LPROPS_UNCAT) {
1040		cat = ubifs_categorize_lprops(c, lp);
1041		if (cat != (lp->flags & LPROPS_CAT_MASK)) {
1042			ubifs_err(c, "bad LEB category %d expected %d",
1043				  (lp->flags & LPROPS_CAT_MASK), cat);
1044			return -EINVAL;
1045		}
1046	}
1047
1048	/* Check lp is on its category list (if it has one) */
1049	if (in_tree) {
1050		struct list_head *list = NULL;
1051
1052		switch (cat) {
1053		case LPROPS_EMPTY:
1054			list = &c->empty_list;
1055			break;
1056		case LPROPS_FREEABLE:
1057			list = &c->freeable_list;
1058			break;
1059		case LPROPS_FRDI_IDX:
1060			list = &c->frdi_idx_list;
1061			break;
1062		case LPROPS_UNCAT:
1063			list = &c->uncat_list;
1064			break;
1065		}
1066		if (list) {
1067			struct ubifs_lprops *lprops;
1068			int found = 0;
1069
1070			list_for_each_entry(lprops, list, list) {
1071				if (lprops == lp) {
1072					found = 1;
1073					break;
1074				}
1075			}
1076			if (!found) {
1077				ubifs_err(c, "bad LPT list (category %d)", cat);
1078				return -EINVAL;
1079			}
1080		}
1081	}
1082
1083	/* Check lp is on its category heap (if it has one) */
1084	if (in_tree && cat > 0 && cat <= LPROPS_HEAP_CNT) {
1085		struct ubifs_lpt_heap *heap = &c->lpt_heap[cat - 1];
1086
1087		if ((lp->hpos != -1 && heap->arr[lp->hpos]->lnum != lnum) ||
1088		    lp != heap->arr[lp->hpos]) {
1089			ubifs_err(c, "bad LPT heap (category %d)", cat);
1090			return -EINVAL;
1091		}
1092	}
1093
1094	buf = __vmalloc(c->leb_size, GFP_NOFS, PAGE_KERNEL);
1095	if (!buf)
1096		return -ENOMEM;
1097
1098	/*
1099	 * After an unclean unmount, empty and freeable LEBs
1100	 * may contain garbage - do not scan them.
1101	 */
1102	if (lp->free == c->leb_size) {
1103		lst->empty_lebs += 1;
1104		lst->total_free += c->leb_size;
1105		lst->total_dark += ubifs_calc_dark(c, c->leb_size);
1106		return LPT_SCAN_CONTINUE;
1107	}
1108	if (lp->free + lp->dirty == c->leb_size &&
1109	    !(lp->flags & LPROPS_INDEX)) {
1110		lst->total_free  += lp->free;
1111		lst->total_dirty += lp->dirty;
1112		lst->total_dark  +=  ubifs_calc_dark(c, c->leb_size);
1113		return LPT_SCAN_CONTINUE;
1114	}
1115
1116	sleb = ubifs_scan(c, lnum, 0, buf, 0);
1117	if (IS_ERR(sleb)) {
1118		ret = PTR_ERR(sleb);
1119		if (ret == -EUCLEAN) {
1120			ubifs_dump_lprops(c);
1121			ubifs_dump_budg(c, &c->bi);
1122		}
1123		goto out;
1124	}
1125
1126	is_idx = -1;
1127	list_for_each_entry(snod, &sleb->nodes, list) {
1128		int found, level = 0;
1129
1130		cond_resched();
1131
1132		if (is_idx == -1)
1133			is_idx = (snod->type == UBIFS_IDX_NODE) ? 1 : 0;
1134
1135		if (is_idx && snod->type != UBIFS_IDX_NODE) {
1136			ubifs_err(c, "indexing node in data LEB %d:%d",
1137				  lnum, snod->offs);
1138			goto out_destroy;
1139		}
1140
1141		if (snod->type == UBIFS_IDX_NODE) {
1142			struct ubifs_idx_node *idx = snod->node;
1143
1144			key_read(c, ubifs_idx_key(c, idx), &snod->key);
1145			level = le16_to_cpu(idx->level);
1146		}
1147
1148		found = ubifs_tnc_has_node(c, &snod->key, level, lnum,
1149					   snod->offs, is_idx);
1150		if (found) {
1151			if (found < 0)
1152				goto out_destroy;
1153			used += ALIGN(snod->len, 8);
1154		}
1155	}
1156
1157	free = c->leb_size - sleb->endpt;
1158	dirty = sleb->endpt - used;
1159
1160	if (free > c->leb_size || free < 0 || dirty > c->leb_size ||
1161	    dirty < 0) {
1162		ubifs_err(c, "bad calculated accounting for LEB %d: free %d, dirty %d",
1163			  lnum, free, dirty);
1164		goto out_destroy;
1165	}
1166
1167	if (lp->free + lp->dirty == c->leb_size &&
1168	    free + dirty == c->leb_size)
1169		if ((is_idx && !(lp->flags & LPROPS_INDEX)) ||
1170		    (!is_idx && free == c->leb_size) ||
1171		    lp->free == c->leb_size) {
1172			/*
1173			 * Empty or freeable LEBs could contain index
1174			 * nodes from an uncompleted commit due to an
1175			 * unclean unmount. Or they could be empty for
1176			 * the same reason. Or it may simply not have been
1177			 * unmapped.
1178			 */
1179			free = lp->free;
1180			dirty = lp->dirty;
1181			is_idx = 0;
1182		    }
1183
1184	if (is_idx && lp->free + lp->dirty == free + dirty &&
1185	    lnum != c->ihead_lnum) {
1186		/*
1187		 * After an unclean unmount, an index LEB could have a different
1188		 * amount of free space than the value recorded by lprops. That
1189		 * is because the in-the-gaps method may use free space or
1190		 * create free space (as a side-effect of using ubi_leb_change
1191		 * and not writing the whole LEB). The incorrect free space
1192		 * value is not a problem because the index is only ever
1193		 * allocated empty LEBs, so there will never be an attempt to
1194		 * write to the free space at the end of an index LEB - except
1195		 * by the in-the-gaps method for which it is not a problem.
1196		 */
1197		free = lp->free;
1198		dirty = lp->dirty;
1199	}
1200
1201	if (lp->free != free || lp->dirty != dirty)
1202		goto out_print;
1203
1204	if (is_idx && !(lp->flags & LPROPS_INDEX)) {
1205		if (free == c->leb_size)
1206			/* Free but not unmapped LEB, it's fine */
1207			is_idx = 0;
1208		else {
1209			ubifs_err(c, "indexing node without indexing flag");
1210			goto out_print;
1211		}
1212	}
1213
1214	if (!is_idx && (lp->flags & LPROPS_INDEX)) {
1215		ubifs_err(c, "data node with indexing flag");
1216		goto out_print;
1217	}
1218
1219	if (free == c->leb_size)
1220		lst->empty_lebs += 1;
1221
1222	if (is_idx)
1223		lst->idx_lebs += 1;
1224
1225	if (!(lp->flags & LPROPS_INDEX))
1226		lst->total_used += c->leb_size - free - dirty;
1227	lst->total_free += free;
1228	lst->total_dirty += dirty;
1229
1230	if (!(lp->flags & LPROPS_INDEX)) {
1231		int spc = free + dirty;
1232
1233		if (spc < c->dead_wm)
1234			lst->total_dead += spc;
1235		else
1236			lst->total_dark += ubifs_calc_dark(c, spc);
1237	}
1238
1239	ubifs_scan_destroy(sleb);
1240	vfree(buf);
1241	return LPT_SCAN_CONTINUE;
1242
1243out_print:
1244	ubifs_err(c, "bad accounting of LEB %d: free %d, dirty %d flags %#x, should be free %d, dirty %d",
1245		  lnum, lp->free, lp->dirty, lp->flags, free, dirty);
1246	ubifs_dump_leb(c, lnum);
1247out_destroy:
1248	ubifs_scan_destroy(sleb);
1249	ret = -EINVAL;
1250out:
1251	vfree(buf);
1252	return ret;
1253}
1254
1255/**
1256 * dbg_check_lprops - check all LEB properties.
1257 * @c: UBIFS file-system description object
1258 *
1259 * This function checks all LEB properties and makes sure they are all correct.
1260 * It returns zero if everything is fine, %-EINVAL if there is an inconsistency
1261 * and other negative error codes in case of other errors. This function is
1262 * called while the file system is locked (because of commit start), so no
1263 * additional locking is required. Note that locking the LPT mutex would cause
1264 * a circular lock dependency with the TNC mutex.
1265 */
1266int dbg_check_lprops(struct ubifs_info *c)
1267{
1268	int i, err;
1269	struct ubifs_lp_stats lst;
1270
1271	if (!dbg_is_chk_lprops(c))
1272		return 0;
1273
1274	/*
1275	 * As we are going to scan the media, the write buffers have to be
1276	 * synchronized.
1277	 */
1278	for (i = 0; i < c->jhead_cnt; i++) {
1279		err = ubifs_wbuf_sync(&c->jheads[i].wbuf);
1280		if (err)
1281			return err;
1282	}
1283
1284	memset(&lst, 0, sizeof(struct ubifs_lp_stats));
1285	err = ubifs_lpt_scan_nolock(c, c->main_first, c->leb_cnt - 1,
1286				    (ubifs_lpt_scan_callback)scan_check_cb,
1287				    &lst);
1288	if (err && err != -ENOSPC)
1289		goto out;
1290
1291	if (lst.empty_lebs != c->lst.empty_lebs ||
1292	    lst.idx_lebs != c->lst.idx_lebs ||
1293	    lst.total_free != c->lst.total_free ||
1294	    lst.total_dirty != c->lst.total_dirty ||
1295	    lst.total_used != c->lst.total_used) {
1296		ubifs_err(c, "bad overall accounting");
1297		ubifs_err(c, "calculated: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
1298			  lst.empty_lebs, lst.idx_lebs, lst.total_free,
1299			  lst.total_dirty, lst.total_used);
1300		ubifs_err(c, "read from lprops: empty_lebs %d, idx_lebs %d, total_free %lld, total_dirty %lld, total_used %lld",
1301			  c->lst.empty_lebs, c->lst.idx_lebs, c->lst.total_free,
1302			  c->lst.total_dirty, c->lst.total_used);
1303		err = -EINVAL;
1304		goto out;
1305	}
1306
1307	if (lst.total_dead != c->lst.total_dead ||
1308	    lst.total_dark != c->lst.total_dark) {
1309		ubifs_err(c, "bad dead/dark space accounting");
1310		ubifs_err(c, "calculated: total_dead %lld, total_dark %lld",
1311			  lst.total_dead, lst.total_dark);
1312		ubifs_err(c, "read from lprops: total_dead %lld, total_dark %lld",
1313			  c->lst.total_dead, c->lst.total_dark);
1314		err = -EINVAL;
1315		goto out;
1316	}
1317
1318	err = dbg_check_cats(c);
1319out:
1320	return err;
1321}
1322