linux/fs/ubifs/lpt.c

2277 lines
59 KiB
C
Raw Normal View History

/*
* This file is part of UBIFS.
*
* Copyright (C) 2006-2008 Nokia Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Authors: Adrian Hunter
* Artem Bityutskiy (Битюцкий Артём)
*/
/*
* This file implements the LEB properties tree (LPT) area. The LPT area
* contains the LEB properties tree, a table of LPT area eraseblocks (ltab), and
* (for the "big" model) a table of saved LEB numbers (lsave). The LPT area sits
* between the log and the orphan area.
*
* The LPT area is like a miniature self-contained file system. It is required
* that it never runs out of space, is fast to access and update, and scales
* logarithmically. The LEB properties tree is implemented as a wandering tree
* much like the TNC, and the LPT area has its own garbage collection.
*
* The LPT has two slightly different forms called the "small model" and the
* "big model". The small model is used when the entire LEB properties table
* can be written into a single eraseblock. In that case, garbage collection
* consists of just writing the whole table, which therefore makes all other
* eraseblocks reusable. In the case of the big model, dirty eraseblocks are
* selected for garbage collection, which consists of marking the clean nodes in
* that LEB as dirty, and then only the dirty nodes are written out. Also, in
* the case of the big model, a table of LEB numbers is saved so that the entire
* LPT does not to be scanned looking for empty eraseblocks when UBIFS is first
* mounted.
*/
#include "ubifs.h"
#include <linux/crc16.h>
#include <linux/math64.h>
include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit slab.h inclusion from percpu.h percpu.h is included by sched.h and module.h and thus ends up being included when building most .c files. percpu.h includes slab.h which in turn includes gfp.h making everything defined by the two files universally available and complicating inclusion dependencies. percpu.h -> slab.h dependency is about to be removed. Prepare for this change by updating users of gfp and slab facilities include those headers directly instead of assuming availability. As this conversion needs to touch large number of source files, the following script is used as the basis of conversion. http://userweb.kernel.org/~tj/misc/slabh-sweep.py The script does the followings. * Scan files for gfp and slab usages and update includes such that only the necessary includes are there. ie. if only gfp is used, gfp.h, if slab is used, slab.h. * When the script inserts a new include, it looks at the include blocks and try to put the new include such that its order conforms to its surrounding. It's put in the include block which contains core kernel includes, in the same order that the rest are ordered - alphabetical, Christmas tree, rev-Xmas-tree or at the end if there doesn't seem to be any matching order. * If the script can't find a place to put a new include (mostly because the file doesn't have fitting include block), it prints out an error message indicating which .h file needs to be added to the file. The conversion was done in the following steps. 1. The initial automatic conversion of all .c files updated slightly over 4000 files, deleting around 700 includes and adding ~480 gfp.h and ~3000 slab.h inclusions. The script emitted errors for ~400 files. 2. Each error was manually checked. Some didn't need the inclusion, some needed manual addition while adding it to implementation .h or embedding .c file was more appropriate for others. This step added inclusions to around 150 files. 3. The script was run again and the output was compared to the edits from #2 to make sure no file was left behind. 4. Several build tests were done and a couple of problems were fixed. e.g. lib/decompress_*.c used malloc/free() wrappers around slab APIs requiring slab.h to be added manually. 5. The script was run on all .h files but without automatically editing them as sprinkling gfp.h and slab.h inclusions around .h files could easily lead to inclusion dependency hell. Most gfp.h inclusion directives were ignored as stuff from gfp.h was usually wildly available and often used in preprocessor macros. Each slab.h inclusion directive was examined and added manually as necessary. 6. percpu.h was updated not to include slab.h. 7. Build test were done on the following configurations and failures were fixed. CONFIG_GCOV_KERNEL was turned off for all tests (as my distributed build env didn't work with gcov compiles) and a few more options had to be turned off depending on archs to make things build (like ipr on powerpc/64 which failed due to missing writeq). * x86 and x86_64 UP and SMP allmodconfig and a custom test config. * powerpc and powerpc64 SMP allmodconfig * sparc and sparc64 SMP allmodconfig * ia64 SMP allmodconfig * s390 SMP allmodconfig * alpha SMP allmodconfig * um on x86_64 SMP allmodconfig 8. percpu.h modifications were reverted so that it could be applied as a separate patch and serve as bisection point. Given the fact that I had only a couple of failures from tests on step 6, I'm fairly confident about the coverage of this conversion patch. If there is a breakage, it's likely to be something in one of the arch headers which should be easily discoverable easily on most builds of the specific arch. Signed-off-by: Tejun Heo <tj@kernel.org> Guess-its-ok-by: Christoph Lameter <cl@linux-foundation.org> Cc: Ingo Molnar <mingo@redhat.com> Cc: Lee Schermerhorn <Lee.Schermerhorn@hp.com>
2010-03-24 11:04:11 +03:00
#include <linux/slab.h>
/**
* do_calc_lpt_geom - calculate sizes for the LPT area.
* @c: the UBIFS file-system description object
*
* Calculate the sizes of LPT bit fields, nodes, and tree, based on the
* properties of the flash and whether LPT is "big" (c->big_lpt).
*/
static void do_calc_lpt_geom(struct ubifs_info *c)
{
int i, n, bits, per_leb_wastage, max_pnode_cnt;
long long sz, tot_wastage;
n = c->main_lebs + c->max_leb_cnt - c->leb_cnt;
max_pnode_cnt = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
c->lpt_hght = 1;
n = UBIFS_LPT_FANOUT;
while (n < max_pnode_cnt) {
c->lpt_hght += 1;
n <<= UBIFS_LPT_FANOUT_SHIFT;
}
c->pnode_cnt = DIV_ROUND_UP(c->main_lebs, UBIFS_LPT_FANOUT);
n = DIV_ROUND_UP(c->pnode_cnt, UBIFS_LPT_FANOUT);
c->nnode_cnt = n;
for (i = 1; i < c->lpt_hght; i++) {
n = DIV_ROUND_UP(n, UBIFS_LPT_FANOUT);
c->nnode_cnt += n;
}
c->space_bits = fls(c->leb_size) - 3;
c->lpt_lnum_bits = fls(c->lpt_lebs);
c->lpt_offs_bits = fls(c->leb_size - 1);
c->lpt_spc_bits = fls(c->leb_size);
n = DIV_ROUND_UP(c->max_leb_cnt, UBIFS_LPT_FANOUT);
c->pcnt_bits = fls(n - 1);
c->lnum_bits = fls(c->max_leb_cnt - 1);
bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
(c->big_lpt ? c->pcnt_bits : 0) +
(c->space_bits * 2 + 1) * UBIFS_LPT_FANOUT;
c->pnode_sz = (bits + 7) / 8;
bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
(c->big_lpt ? c->pcnt_bits : 0) +
(c->lpt_lnum_bits + c->lpt_offs_bits) * UBIFS_LPT_FANOUT;
c->nnode_sz = (bits + 7) / 8;
bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
c->lpt_lebs * c->lpt_spc_bits * 2;
c->ltab_sz = (bits + 7) / 8;
bits = UBIFS_LPT_CRC_BITS + UBIFS_LPT_TYPE_BITS +
c->lnum_bits * c->lsave_cnt;
c->lsave_sz = (bits + 7) / 8;
/* Calculate the minimum LPT size */
c->lpt_sz = (long long)c->pnode_cnt * c->pnode_sz;
c->lpt_sz += (long long)c->nnode_cnt * c->nnode_sz;
c->lpt_sz += c->ltab_sz;
if (c->big_lpt)
c->lpt_sz += c->lsave_sz;
/* Add wastage */
sz = c->lpt_sz;
per_leb_wastage = max_t(int, c->pnode_sz, c->nnode_sz);
sz += per_leb_wastage;
tot_wastage = per_leb_wastage;
while (sz > c->leb_size) {
sz += per_leb_wastage;
sz -= c->leb_size;
tot_wastage += per_leb_wastage;
}
tot_wastage += ALIGN(sz, c->min_io_size) - sz;
c->lpt_sz += tot_wastage;
}
/**
* ubifs_calc_lpt_geom - calculate and check sizes for the LPT area.
* @c: the UBIFS file-system description object
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_calc_lpt_geom(struct ubifs_info *c)
{
int lebs_needed;
long long sz;
do_calc_lpt_geom(c);
/* Verify that lpt_lebs is big enough */
sz = c->lpt_sz * 2; /* Must have at least 2 times the size */
lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
if (lebs_needed > c->lpt_lebs) {
ubifs_err("too few LPT LEBs");
return -EINVAL;
}
/* Verify that ltab fits in a single LEB (since ltab is a single node */
if (c->ltab_sz > c->leb_size) {
ubifs_err("LPT ltab too big");
return -EINVAL;
}
c->check_lpt_free = c->big_lpt;
return 0;
}
/**
* calc_dflt_lpt_geom - calculate default LPT geometry.
* @c: the UBIFS file-system description object
* @main_lebs: number of main area LEBs is passed and returned here
* @big_lpt: whether the LPT area is "big" is returned here
*
* The size of the LPT area depends on parameters that themselves are dependent
* on the size of the LPT area. This function, successively recalculates the LPT
* area geometry until the parameters and resultant geometry are consistent.
*
* This function returns %0 on success and a negative error code on failure.
*/
static int calc_dflt_lpt_geom(struct ubifs_info *c, int *main_lebs,
int *big_lpt)
{
int i, lebs_needed;
long long sz;
/* Start by assuming the minimum number of LPT LEBs */
c->lpt_lebs = UBIFS_MIN_LPT_LEBS;
c->main_lebs = *main_lebs - c->lpt_lebs;
if (c->main_lebs <= 0)
return -EINVAL;
/* And assume we will use the small LPT model */
c->big_lpt = 0;
/*
* Calculate the geometry based on assumptions above and then see if it
* makes sense
*/
do_calc_lpt_geom(c);
/* Small LPT model must have lpt_sz < leb_size */
if (c->lpt_sz > c->leb_size) {
/* Nope, so try again using big LPT model */
c->big_lpt = 1;
do_calc_lpt_geom(c);
}
/* Now check there are enough LPT LEBs */
for (i = 0; i < 64 ; i++) {
sz = c->lpt_sz * 4; /* Allow 4 times the size */
lebs_needed = div_u64(sz + c->leb_size - 1, c->leb_size);
if (lebs_needed > c->lpt_lebs) {
/* Not enough LPT LEBs so try again with more */
c->lpt_lebs = lebs_needed;
c->main_lebs = *main_lebs - c->lpt_lebs;
if (c->main_lebs <= 0)
return -EINVAL;
do_calc_lpt_geom(c);
continue;
}
if (c->ltab_sz > c->leb_size) {
ubifs_err("LPT ltab too big");
return -EINVAL;
}
*main_lebs = c->main_lebs;
*big_lpt = c->big_lpt;
return 0;
}
return -EINVAL;
}
/**
* pack_bits - pack bit fields end-to-end.
* @addr: address at which to pack (passed and next address returned)
* @pos: bit position at which to pack (passed and next position returned)
* @val: value to pack
* @nrbits: number of bits of value to pack (1-32)
*/
static void pack_bits(uint8_t **addr, int *pos, uint32_t val, int nrbits)
{
uint8_t *p = *addr;
int b = *pos;
ubifs_assert(nrbits > 0);
ubifs_assert(nrbits <= 32);
ubifs_assert(*pos >= 0);
ubifs_assert(*pos < 8);
ubifs_assert((val >> nrbits) == 0 || nrbits == 32);
if (b) {
*p |= ((uint8_t)val) << b;
nrbits += b;
if (nrbits > 8) {
*++p = (uint8_t)(val >>= (8 - b));
if (nrbits > 16) {
*++p = (uint8_t)(val >>= 8);
if (nrbits > 24) {
*++p = (uint8_t)(val >>= 8);
if (nrbits > 32)
*++p = (uint8_t)(val >>= 8);
}
}
}
} else {
*p = (uint8_t)val;
if (nrbits > 8) {
*++p = (uint8_t)(val >>= 8);
if (nrbits > 16) {
*++p = (uint8_t)(val >>= 8);
if (nrbits > 24)
*++p = (uint8_t)(val >>= 8);
}
}
}
b = nrbits & 7;
if (b == 0)
p++;
*addr = p;
*pos = b;
}
/**
* ubifs_unpack_bits - unpack bit fields.
* @addr: address at which to unpack (passed and next address returned)
* @pos: bit position at which to unpack (passed and next position returned)
* @nrbits: number of bits of value to unpack (1-32)
*
* This functions returns the value unpacked.
*/
uint32_t ubifs_unpack_bits(uint8_t **addr, int *pos, int nrbits)
{
const int k = 32 - nrbits;
uint8_t *p = *addr;
int b = *pos;
UBIFS: do not read unnecessary bytes when unpacking bits Fixes the following Oops: BUG: unable to handle kernel paging request at f8d24000 IP: [<f8ff0657>] :ubifs:ubifs_unpack_bits+0xcd/0x231 *pde = 34333067 *pte = 00000000 Oops: 0000 [#1] PREEMPT SMP Modules linked in: deflate zlib_deflate lzo lzo_decompress lzo_compress ubifs ubi nandsim nand nand_ids nand_ecc mtd nfsd lockd sunrpc exportfs [last unloaded: nand_ecc] Pid: 7450, comm: sync Not tainted (2.6.27-rc8-ubifs-2.6 #27) EIP: 0060:[<f8ff0657>] EFLAGS: 00010206 CPU: 0 EIP is at ubifs_unpack_bits+0xcd/0x231 [ubifs] EAX: 00000000 EBX: 00000000 ECX: d7e43dc0 EDX: 0000ff00 ESI: 00000004 EDI: f8d23ffe EBP: d7e43db4 ESP: d7e43d8c DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process sync (pid: 7450, ti=d7e42000 task=eb6f9530 task.ti=d7e42000) Stack: 00000400 c0103db4 dc5e8090 d7e43dc0 d7e43dc0 d7e43dc4 0000001c 00000004 f496d1e0 f8d23ffc d7e43dd4 f8ffac7e f8d23ffe 00000000 f8d23ffe f2b7af68 f496d1e0 f8d23ffc d7e43e2c f8ffadc5 00000000 0001f000 00000000 c03b10a7 Call Trace: [<c0103db4>] ? restore_nocheck_notrace+0x0/0xe [<f8ffac7e>] ? is_a_node+0x43/0x92 [ubifs] [<f8ffadc5>] ? dbg_check_ltab+0xf8/0x5c9 [ubifs] [<c03b10a7>] ? mutex_lock_nested+0x1b2/0x2a0 [<f8ffc86e>] ? ubifs_lpt_start_commit+0x49/0xecb [ubifs] [<c03b0ef3>] ? mutex_unlock+0xd/0xf [<f8fef017>] ? ubifs_tnc_start_commit+0x1cf/0xef8 [ubifs] [<f8fe65d8>] ? do_commit+0x18f/0x52d [ubifs] [<f8fe69f6>] ? ubifs_run_commit+0x80/0xca [ubifs] [<f8fd8d35>] ? ubifs_sync_fs+0xdb/0xf6 [ubifs] [<c0181a07>] ? sync_filesystems+0xc6/0x10c [<c019f279>] ? do_sync+0x3b/0x6a [<c019f2ba>] ? sys_sync+0x12/0x18 [<c0103ced>] ? sysenter_do_call+0x12/0x35 ======================= Code: 4d ec 89 01 8b 45 e8 89 10 89 d8 89 f1 d3 e8 85 c0 74 07 29 d6 83 fe 20 75 2a 89 d8 83 c4 1c 5b 5e 5f 5d c3 0f b6 57 01 c1 e2 08 <0f> b6 47 02 c1 e0 10 09 c2 0f b6 07 09 c2 0f b EIP: [<f8ff0657>] ubifs_unpack_bits+0xcd/0x231 [ubifs] SS:ESP 0068:d7e43d8c ---[ end trace 1bbb4c407a6dd816 ]--- Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
2008-10-17 17:52:10 +04:00
uint32_t uninitialized_var(val);
const int bytes = (nrbits + b + 7) >> 3;
ubifs_assert(nrbits > 0);
ubifs_assert(nrbits <= 32);
ubifs_assert(*pos >= 0);
ubifs_assert(*pos < 8);
if (b) {
UBIFS: do not read unnecessary bytes when unpacking bits Fixes the following Oops: BUG: unable to handle kernel paging request at f8d24000 IP: [<f8ff0657>] :ubifs:ubifs_unpack_bits+0xcd/0x231 *pde = 34333067 *pte = 00000000 Oops: 0000 [#1] PREEMPT SMP Modules linked in: deflate zlib_deflate lzo lzo_decompress lzo_compress ubifs ubi nandsim nand nand_ids nand_ecc mtd nfsd lockd sunrpc exportfs [last unloaded: nand_ecc] Pid: 7450, comm: sync Not tainted (2.6.27-rc8-ubifs-2.6 #27) EIP: 0060:[<f8ff0657>] EFLAGS: 00010206 CPU: 0 EIP is at ubifs_unpack_bits+0xcd/0x231 [ubifs] EAX: 00000000 EBX: 00000000 ECX: d7e43dc0 EDX: 0000ff00 ESI: 00000004 EDI: f8d23ffe EBP: d7e43db4 ESP: d7e43d8c DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process sync (pid: 7450, ti=d7e42000 task=eb6f9530 task.ti=d7e42000) Stack: 00000400 c0103db4 dc5e8090 d7e43dc0 d7e43dc0 d7e43dc4 0000001c 00000004 f496d1e0 f8d23ffc d7e43dd4 f8ffac7e f8d23ffe 00000000 f8d23ffe f2b7af68 f496d1e0 f8d23ffc d7e43e2c f8ffadc5 00000000 0001f000 00000000 c03b10a7 Call Trace: [<c0103db4>] ? restore_nocheck_notrace+0x0/0xe [<f8ffac7e>] ? is_a_node+0x43/0x92 [ubifs] [<f8ffadc5>] ? dbg_check_ltab+0xf8/0x5c9 [ubifs] [<c03b10a7>] ? mutex_lock_nested+0x1b2/0x2a0 [<f8ffc86e>] ? ubifs_lpt_start_commit+0x49/0xecb [ubifs] [<c03b0ef3>] ? mutex_unlock+0xd/0xf [<f8fef017>] ? ubifs_tnc_start_commit+0x1cf/0xef8 [ubifs] [<f8fe65d8>] ? do_commit+0x18f/0x52d [ubifs] [<f8fe69f6>] ? ubifs_run_commit+0x80/0xca [ubifs] [<f8fd8d35>] ? ubifs_sync_fs+0xdb/0xf6 [ubifs] [<c0181a07>] ? sync_filesystems+0xc6/0x10c [<c019f279>] ? do_sync+0x3b/0x6a [<c019f2ba>] ? sys_sync+0x12/0x18 [<c0103ced>] ? sysenter_do_call+0x12/0x35 ======================= Code: 4d ec 89 01 8b 45 e8 89 10 89 d8 89 f1 d3 e8 85 c0 74 07 29 d6 83 fe 20 75 2a 89 d8 83 c4 1c 5b 5e 5f 5d c3 0f b6 57 01 c1 e2 08 <0f> b6 47 02 c1 e0 10 09 c2 0f b6 07 09 c2 0f b EIP: [<f8ff0657>] ubifs_unpack_bits+0xcd/0x231 [ubifs] SS:ESP 0068:d7e43d8c ---[ end trace 1bbb4c407a6dd816 ]--- Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
2008-10-17 17:52:10 +04:00
switch (bytes) {
case 2:
val = p[1];
break;
case 3:
val = p[1] | ((uint32_t)p[2] << 8);
break;
case 4:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16);
break;
case 5:
val = p[1] | ((uint32_t)p[2] << 8) |
((uint32_t)p[3] << 16) |
((uint32_t)p[4] << 24);
}
val <<= (8 - b);
val |= *p >> b;
nrbits += b;
UBIFS: do not read unnecessary bytes when unpacking bits Fixes the following Oops: BUG: unable to handle kernel paging request at f8d24000 IP: [<f8ff0657>] :ubifs:ubifs_unpack_bits+0xcd/0x231 *pde = 34333067 *pte = 00000000 Oops: 0000 [#1] PREEMPT SMP Modules linked in: deflate zlib_deflate lzo lzo_decompress lzo_compress ubifs ubi nandsim nand nand_ids nand_ecc mtd nfsd lockd sunrpc exportfs [last unloaded: nand_ecc] Pid: 7450, comm: sync Not tainted (2.6.27-rc8-ubifs-2.6 #27) EIP: 0060:[<f8ff0657>] EFLAGS: 00010206 CPU: 0 EIP is at ubifs_unpack_bits+0xcd/0x231 [ubifs] EAX: 00000000 EBX: 00000000 ECX: d7e43dc0 EDX: 0000ff00 ESI: 00000004 EDI: f8d23ffe EBP: d7e43db4 ESP: d7e43d8c DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process sync (pid: 7450, ti=d7e42000 task=eb6f9530 task.ti=d7e42000) Stack: 00000400 c0103db4 dc5e8090 d7e43dc0 d7e43dc0 d7e43dc4 0000001c 00000004 f496d1e0 f8d23ffc d7e43dd4 f8ffac7e f8d23ffe 00000000 f8d23ffe f2b7af68 f496d1e0 f8d23ffc d7e43e2c f8ffadc5 00000000 0001f000 00000000 c03b10a7 Call Trace: [<c0103db4>] ? restore_nocheck_notrace+0x0/0xe [<f8ffac7e>] ? is_a_node+0x43/0x92 [ubifs] [<f8ffadc5>] ? dbg_check_ltab+0xf8/0x5c9 [ubifs] [<c03b10a7>] ? mutex_lock_nested+0x1b2/0x2a0 [<f8ffc86e>] ? ubifs_lpt_start_commit+0x49/0xecb [ubifs] [<c03b0ef3>] ? mutex_unlock+0xd/0xf [<f8fef017>] ? ubifs_tnc_start_commit+0x1cf/0xef8 [ubifs] [<f8fe65d8>] ? do_commit+0x18f/0x52d [ubifs] [<f8fe69f6>] ? ubifs_run_commit+0x80/0xca [ubifs] [<f8fd8d35>] ? ubifs_sync_fs+0xdb/0xf6 [ubifs] [<c0181a07>] ? sync_filesystems+0xc6/0x10c [<c019f279>] ? do_sync+0x3b/0x6a [<c019f2ba>] ? sys_sync+0x12/0x18 [<c0103ced>] ? sysenter_do_call+0x12/0x35 ======================= Code: 4d ec 89 01 8b 45 e8 89 10 89 d8 89 f1 d3 e8 85 c0 74 07 29 d6 83 fe 20 75 2a 89 d8 83 c4 1c 5b 5e 5f 5d c3 0f b6 57 01 c1 e2 08 <0f> b6 47 02 c1 e0 10 09 c2 0f b6 07 09 c2 0f b EIP: [<f8ff0657>] ubifs_unpack_bits+0xcd/0x231 [ubifs] SS:ESP 0068:d7e43d8c ---[ end trace 1bbb4c407a6dd816 ]--- Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
2008-10-17 17:52:10 +04:00
} else {
switch (bytes) {
case 1:
val = p[0];
break;
case 2:
val = p[0] | ((uint32_t)p[1] << 8);
break;
case 3:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16);
break;
case 4:
val = p[0] | ((uint32_t)p[1] << 8) |
((uint32_t)p[2] << 16) |
((uint32_t)p[3] << 24);
break;
}
}
val <<= k;
val >>= k;
b = nrbits & 7;
UBIFS: do not read unnecessary bytes when unpacking bits Fixes the following Oops: BUG: unable to handle kernel paging request at f8d24000 IP: [<f8ff0657>] :ubifs:ubifs_unpack_bits+0xcd/0x231 *pde = 34333067 *pte = 00000000 Oops: 0000 [#1] PREEMPT SMP Modules linked in: deflate zlib_deflate lzo lzo_decompress lzo_compress ubifs ubi nandsim nand nand_ids nand_ecc mtd nfsd lockd sunrpc exportfs [last unloaded: nand_ecc] Pid: 7450, comm: sync Not tainted (2.6.27-rc8-ubifs-2.6 #27) EIP: 0060:[<f8ff0657>] EFLAGS: 00010206 CPU: 0 EIP is at ubifs_unpack_bits+0xcd/0x231 [ubifs] EAX: 00000000 EBX: 00000000 ECX: d7e43dc0 EDX: 0000ff00 ESI: 00000004 EDI: f8d23ffe EBP: d7e43db4 ESP: d7e43d8c DS: 007b ES: 007b FS: 00d8 GS: 0033 SS: 0068 Process sync (pid: 7450, ti=d7e42000 task=eb6f9530 task.ti=d7e42000) Stack: 00000400 c0103db4 dc5e8090 d7e43dc0 d7e43dc0 d7e43dc4 0000001c 00000004 f496d1e0 f8d23ffc d7e43dd4 f8ffac7e f8d23ffe 00000000 f8d23ffe f2b7af68 f496d1e0 f8d23ffc d7e43e2c f8ffadc5 00000000 0001f000 00000000 c03b10a7 Call Trace: [<c0103db4>] ? restore_nocheck_notrace+0x0/0xe [<f8ffac7e>] ? is_a_node+0x43/0x92 [ubifs] [<f8ffadc5>] ? dbg_check_ltab+0xf8/0x5c9 [ubifs] [<c03b10a7>] ? mutex_lock_nested+0x1b2/0x2a0 [<f8ffc86e>] ? ubifs_lpt_start_commit+0x49/0xecb [ubifs] [<c03b0ef3>] ? mutex_unlock+0xd/0xf [<f8fef017>] ? ubifs_tnc_start_commit+0x1cf/0xef8 [ubifs] [<f8fe65d8>] ? do_commit+0x18f/0x52d [ubifs] [<f8fe69f6>] ? ubifs_run_commit+0x80/0xca [ubifs] [<f8fd8d35>] ? ubifs_sync_fs+0xdb/0xf6 [ubifs] [<c0181a07>] ? sync_filesystems+0xc6/0x10c [<c019f279>] ? do_sync+0x3b/0x6a [<c019f2ba>] ? sys_sync+0x12/0x18 [<c0103ced>] ? sysenter_do_call+0x12/0x35 ======================= Code: 4d ec 89 01 8b 45 e8 89 10 89 d8 89 f1 d3 e8 85 c0 74 07 29 d6 83 fe 20 75 2a 89 d8 83 c4 1c 5b 5e 5f 5d c3 0f b6 57 01 c1 e2 08 <0f> b6 47 02 c1 e0 10 09 c2 0f b6 07 09 c2 0f b EIP: [<f8ff0657>] ubifs_unpack_bits+0xcd/0x231 [ubifs] SS:ESP 0068:d7e43d8c ---[ end trace 1bbb4c407a6dd816 ]--- Signed-off-by: Adrian Hunter <ext-adrian.hunter@nokia.com>
2008-10-17 17:52:10 +04:00
p += nrbits >> 3;
*addr = p;
*pos = b;
ubifs_assert((val >> nrbits) == 0 || nrbits - b == 32);
return val;
}
/**
* ubifs_pack_pnode - pack all the bit fields of a pnode.
* @c: UBIFS file-system description object
* @buf: buffer into which to pack
* @pnode: pnode to pack
*/
void ubifs_pack_pnode(struct ubifs_info *c, void *buf,
struct ubifs_pnode *pnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0;
uint16_t crc;
pack_bits(&addr, &pos, UBIFS_LPT_PNODE, UBIFS_LPT_TYPE_BITS);
if (c->big_lpt)
pack_bits(&addr, &pos, pnode->num, c->pcnt_bits);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
pack_bits(&addr, &pos, pnode->lprops[i].free >> 3,
c->space_bits);
pack_bits(&addr, &pos, pnode->lprops[i].dirty >> 3,
c->space_bits);
if (pnode->lprops[i].flags & LPROPS_INDEX)
pack_bits(&addr, &pos, 1, 1);
else
pack_bits(&addr, &pos, 0, 1);
}
crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
c->pnode_sz - UBIFS_LPT_CRC_BYTES);
addr = buf;
pos = 0;
pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
}
/**
* ubifs_pack_nnode - pack all the bit fields of a nnode.
* @c: UBIFS file-system description object
* @buf: buffer into which to pack
* @nnode: nnode to pack
*/
void ubifs_pack_nnode(struct ubifs_info *c, void *buf,
struct ubifs_nnode *nnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0;
uint16_t crc;
pack_bits(&addr, &pos, UBIFS_LPT_NNODE, UBIFS_LPT_TYPE_BITS);
if (c->big_lpt)
pack_bits(&addr, &pos, nnode->num, c->pcnt_bits);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int lnum = nnode->nbranch[i].lnum;
if (lnum == 0)
lnum = c->lpt_last + 1;
pack_bits(&addr, &pos, lnum - c->lpt_first, c->lpt_lnum_bits);
pack_bits(&addr, &pos, nnode->nbranch[i].offs,
c->lpt_offs_bits);
}
crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
c->nnode_sz - UBIFS_LPT_CRC_BYTES);
addr = buf;
pos = 0;
pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
}
/**
* ubifs_pack_ltab - pack the LPT's own lprops table.
* @c: UBIFS file-system description object
* @buf: buffer into which to pack
* @ltab: LPT's own lprops table to pack
*/
void ubifs_pack_ltab(struct ubifs_info *c, void *buf,
struct ubifs_lpt_lprops *ltab)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0;
uint16_t crc;
pack_bits(&addr, &pos, UBIFS_LPT_LTAB, UBIFS_LPT_TYPE_BITS);
for (i = 0; i < c->lpt_lebs; i++) {
pack_bits(&addr, &pos, ltab[i].free, c->lpt_spc_bits);
pack_bits(&addr, &pos, ltab[i].dirty, c->lpt_spc_bits);
}
crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
c->ltab_sz - UBIFS_LPT_CRC_BYTES);
addr = buf;
pos = 0;
pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
}
/**
* ubifs_pack_lsave - pack the LPT's save table.
* @c: UBIFS file-system description object
* @buf: buffer into which to pack
* @lsave: LPT's save table to pack
*/
void ubifs_pack_lsave(struct ubifs_info *c, void *buf, int *lsave)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0;
uint16_t crc;
pack_bits(&addr, &pos, UBIFS_LPT_LSAVE, UBIFS_LPT_TYPE_BITS);
for (i = 0; i < c->lsave_cnt; i++)
pack_bits(&addr, &pos, lsave[i], c->lnum_bits);
crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
c->lsave_sz - UBIFS_LPT_CRC_BYTES);
addr = buf;
pos = 0;
pack_bits(&addr, &pos, crc, UBIFS_LPT_CRC_BITS);
}
/**
* ubifs_add_lpt_dirt - add dirty space to LPT LEB properties.
* @c: UBIFS file-system description object
* @lnum: LEB number to which to add dirty space
* @dirty: amount of dirty space to add
*/
void ubifs_add_lpt_dirt(struct ubifs_info *c, int lnum, int dirty)
{
if (!dirty || !lnum)
return;
dbg_lp("LEB %d add %d to %d",
lnum, dirty, c->ltab[lnum - c->lpt_first].dirty);
ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last);
c->ltab[lnum - c->lpt_first].dirty += dirty;
}
/**
* set_ltab - set LPT LEB properties.
* @c: UBIFS file-system description object
* @lnum: LEB number
* @free: amount of free space
* @dirty: amount of dirty space
*/
static void set_ltab(struct ubifs_info *c, int lnum, int free, int dirty)
{
dbg_lp("LEB %d free %d dirty %d to %d %d",
lnum, c->ltab[lnum - c->lpt_first].free,
c->ltab[lnum - c->lpt_first].dirty, free, dirty);
ubifs_assert(lnum >= c->lpt_first && lnum <= c->lpt_last);
c->ltab[lnum - c->lpt_first].free = free;
c->ltab[lnum - c->lpt_first].dirty = dirty;
}
/**
* ubifs_add_nnode_dirt - add dirty space to LPT LEB properties.
* @c: UBIFS file-system description object
* @nnode: nnode for which to add dirt
*/
void ubifs_add_nnode_dirt(struct ubifs_info *c, struct ubifs_nnode *nnode)
{
struct ubifs_nnode *np = nnode->parent;
if (np)
ubifs_add_lpt_dirt(c, np->nbranch[nnode->iip].lnum,
c->nnode_sz);
else {
ubifs_add_lpt_dirt(c, c->lpt_lnum, c->nnode_sz);
if (!(c->lpt_drty_flgs & LTAB_DIRTY)) {
c->lpt_drty_flgs |= LTAB_DIRTY;
ubifs_add_lpt_dirt(c, c->ltab_lnum, c->ltab_sz);
}
}
}
/**
* add_pnode_dirt - add dirty space to LPT LEB properties.
* @c: UBIFS file-system description object
* @pnode: pnode for which to add dirt
*/
static void add_pnode_dirt(struct ubifs_info *c, struct ubifs_pnode *pnode)
{
ubifs_add_lpt_dirt(c, pnode->parent->nbranch[pnode->iip].lnum,
c->pnode_sz);
}
/**
* calc_nnode_num - calculate nnode number.
* @row: the row in the tree (root is zero)
* @col: the column in the row (leftmost is zero)
*
* The nnode number is a number that uniquely identifies a nnode and can be used
* easily to traverse the tree from the root to that nnode.
*
* This function calculates and returns the nnode number for the nnode at @row
* and @col.
*/
static int calc_nnode_num(int row, int col)
{
int num, bits;
num = 1;
while (row--) {
bits = (col & (UBIFS_LPT_FANOUT - 1));
col >>= UBIFS_LPT_FANOUT_SHIFT;
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= bits;
}
return num;
}
/**
* calc_nnode_num_from_parent - calculate nnode number.
* @c: UBIFS file-system description object
* @parent: parent nnode
* @iip: index in parent
*
* The nnode number is a number that uniquely identifies a nnode and can be used
* easily to traverse the tree from the root to that nnode.
*
* This function calculates and returns the nnode number based on the parent's
* nnode number and the index in parent.
*/
static int calc_nnode_num_from_parent(const struct ubifs_info *c,
struct ubifs_nnode *parent, int iip)
{
int num, shft;
if (!parent)
return 1;
shft = (c->lpt_hght - parent->level) * UBIFS_LPT_FANOUT_SHIFT;
num = parent->num ^ (1 << shft);
num |= (UBIFS_LPT_FANOUT + iip) << shft;
return num;
}
/**
* calc_pnode_num_from_parent - calculate pnode number.
* @c: UBIFS file-system description object
* @parent: parent nnode
* @iip: index in parent
*
* The pnode number is a number that uniquely identifies a pnode and can be used
* easily to traverse the tree from the root to that pnode.
*
* This function calculates and returns the pnode number based on the parent's
* nnode number and the index in parent.
*/
static int calc_pnode_num_from_parent(const struct ubifs_info *c,
struct ubifs_nnode *parent, int iip)
{
int i, n = c->lpt_hght - 1, pnum = parent->num, num = 0;
for (i = 0; i < n; i++) {
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= pnum & (UBIFS_LPT_FANOUT - 1);
pnum >>= UBIFS_LPT_FANOUT_SHIFT;
}
num <<= UBIFS_LPT_FANOUT_SHIFT;
num |= iip;
return num;
}
/**
* ubifs_create_dflt_lpt - create default LPT.
* @c: UBIFS file-system description object
* @main_lebs: number of main area LEBs is passed and returned here
* @lpt_first: LEB number of first LPT LEB
* @lpt_lebs: number of LEBs for LPT is passed and returned here
* @big_lpt: use big LPT model is passed and returned here
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_create_dflt_lpt(struct ubifs_info *c, int *main_lebs, int lpt_first,
int *lpt_lebs, int *big_lpt)
{
int lnum, err = 0, node_sz, iopos, i, j, cnt, len, alen, row;
int blnum, boffs, bsz, bcnt;
struct ubifs_pnode *pnode = NULL;
struct ubifs_nnode *nnode = NULL;
void *buf = NULL, *p;
struct ubifs_lpt_lprops *ltab = NULL;
int *lsave = NULL;
err = calc_dflt_lpt_geom(c, main_lebs, big_lpt);
if (err)
return err;
*lpt_lebs = c->lpt_lebs;
/* Needed by 'ubifs_pack_nnode()' and 'set_ltab()' */
c->lpt_first = lpt_first;
/* Needed by 'set_ltab()' */
c->lpt_last = lpt_first + c->lpt_lebs - 1;
/* Needed by 'ubifs_pack_lsave()' */
c->main_first = c->leb_cnt - *main_lebs;
lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_KERNEL);
pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_KERNEL);
nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_KERNEL);
buf = vmalloc(c->leb_size);
ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
if (!pnode || !nnode || !buf || !ltab || !lsave) {
err = -ENOMEM;
goto out;
}
ubifs_assert(!c->ltab);
c->ltab = ltab; /* Needed by set_ltab */
/* Initialize LPT's own lprops */
for (i = 0; i < c->lpt_lebs; i++) {
ltab[i].free = c->leb_size;
ltab[i].dirty = 0;
ltab[i].tgc = 0;
ltab[i].cmt = 0;
}
lnum = lpt_first;
p = buf;
/* Number of leaf nodes (pnodes) */
cnt = c->pnode_cnt;
/*
* The first pnode contains the LEB properties for the LEBs that contain
* the root inode node and the root index node of the index tree.
*/
node_sz = ALIGN(ubifs_idx_node_sz(c, 1), 8);
iopos = ALIGN(node_sz, c->min_io_size);
pnode->lprops[0].free = c->leb_size - iopos;
pnode->lprops[0].dirty = iopos - node_sz;
pnode->lprops[0].flags = LPROPS_INDEX;
node_sz = UBIFS_INO_NODE_SZ;
iopos = ALIGN(node_sz, c->min_io_size);
pnode->lprops[1].free = c->leb_size - iopos;
pnode->lprops[1].dirty = iopos - node_sz;
for (i = 2; i < UBIFS_LPT_FANOUT; i++)
pnode->lprops[i].free = c->leb_size;
/* Add first pnode */
ubifs_pack_pnode(c, p, pnode);
p += c->pnode_sz;
len = c->pnode_sz;
pnode->num += 1;
/* Reset pnode values for remaining pnodes */
pnode->lprops[0].free = c->leb_size;
pnode->lprops[0].dirty = 0;
pnode->lprops[0].flags = 0;
pnode->lprops[1].free = c->leb_size;
pnode->lprops[1].dirty = 0;
/*
* To calculate the internal node branches, we keep information about
* the level below.
*/
blnum = lnum; /* LEB number of level below */
boffs = 0; /* Offset of level below */
bcnt = cnt; /* Number of nodes in level below */
bsz = c->pnode_sz; /* Size of nodes in level below */
/* Add all remaining pnodes */
for (i = 1; i < cnt; i++) {
if (len + c->pnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen);
if (err)
goto out;
p = buf;
len = 0;
}
ubifs_pack_pnode(c, p, pnode);
p += c->pnode_sz;
len += c->pnode_sz;
/*
* pnodes are simply numbered left to right starting at zero,
* which means the pnode number can be used easily to traverse
* down the tree to the corresponding pnode.
*/
pnode->num += 1;
}
row = 0;
for (i = UBIFS_LPT_FANOUT; cnt > i; i <<= UBIFS_LPT_FANOUT_SHIFT)
row += 1;
/* Add all nnodes, one level at a time */
while (1) {
/* Number of internal nodes (nnodes) at next level */
cnt = DIV_ROUND_UP(cnt, UBIFS_LPT_FANOUT);
for (i = 0; i < cnt; i++) {
if (len + c->nnode_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen,
alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen);
if (err)
goto out;
p = buf;
len = 0;
}
/* Only 1 nnode at this level, so it is the root */
if (cnt == 1) {
c->lpt_lnum = lnum;
c->lpt_offs = len;
}
/* Set branches to the level below */
for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
if (bcnt) {
if (boffs + bsz > c->leb_size) {
blnum += 1;
boffs = 0;
}
nnode->nbranch[j].lnum = blnum;
nnode->nbranch[j].offs = boffs;
boffs += bsz;
bcnt--;
} else {
nnode->nbranch[j].lnum = 0;
nnode->nbranch[j].offs = 0;
}
}
nnode->num = calc_nnode_num(row, i);
ubifs_pack_nnode(c, p, nnode);
p += c->nnode_sz;
len += c->nnode_sz;
}
/* Only 1 nnode at this level, so it is the root */
if (cnt == 1)
break;
/* Update the information about the level below */
bcnt = cnt;
bsz = c->nnode_sz;
row -= 1;
}
if (*big_lpt) {
/* Need to add LPT's save table */
if (len + c->lsave_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen);
if (err)
goto out;
p = buf;
len = 0;
}
c->lsave_lnum = lnum;
c->lsave_offs = len;
for (i = 0; i < c->lsave_cnt && i < *main_lebs; i++)
lsave[i] = c->main_first + i;
for (; i < c->lsave_cnt; i++)
lsave[i] = c->main_first;
ubifs_pack_lsave(c, p, lsave);
p += c->lsave_sz;
len += c->lsave_sz;
}
/* Need to add LPT's own LEB properties table */
if (len + c->ltab_sz > c->leb_size) {
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum++, buf, alen);
if (err)
goto out;
p = buf;
len = 0;
}
c->ltab_lnum = lnum;
c->ltab_offs = len;
/* Update ltab before packing it */
len += c->ltab_sz;
alen = ALIGN(len, c->min_io_size);
set_ltab(c, lnum, c->leb_size - alen, alen - len);
ubifs_pack_ltab(c, p, ltab);
p += c->ltab_sz;
/* Write remaining buffer */
memset(p, 0xff, alen - len);
err = ubifs_leb_change(c, lnum, buf, alen);
if (err)
goto out;
c->nhead_lnum = lnum;
c->nhead_offs = ALIGN(len, c->min_io_size);
dbg_lp("space_bits %d", c->space_bits);
dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
dbg_lp("pcnt_bits %d", c->pcnt_bits);
dbg_lp("lnum_bits %d", c->lnum_bits);
dbg_lp("pnode_sz %d", c->pnode_sz);
dbg_lp("nnode_sz %d", c->nnode_sz);
dbg_lp("ltab_sz %d", c->ltab_sz);
dbg_lp("lsave_sz %d", c->lsave_sz);
dbg_lp("lsave_cnt %d", c->lsave_cnt);
dbg_lp("lpt_hght %d", c->lpt_hght);
dbg_lp("big_lpt %d", c->big_lpt);
dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
out:
c->ltab = NULL;
kfree(lsave);
vfree(ltab);
vfree(buf);
kfree(nnode);
kfree(pnode);
return err;
}
/**
* update_cats - add LEB properties of a pnode to LEB category lists and heaps.
* @c: UBIFS file-system description object
* @pnode: pnode
*
* When a pnode is loaded into memory, the LEB properties it contains are added,
* by this function, to the LEB category lists and heaps.
*/
static void update_cats(struct ubifs_info *c, struct ubifs_pnode *pnode)
{
int i;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int cat = pnode->lprops[i].flags & LPROPS_CAT_MASK;
int lnum = pnode->lprops[i].lnum;
if (!lnum)
return;
ubifs_add_to_cat(c, &pnode->lprops[i], cat);
}
}
/**
* replace_cats - add LEB properties of a pnode to LEB category lists and heaps.
* @c: UBIFS file-system description object
* @old_pnode: pnode copied
* @new_pnode: pnode copy
*
* During commit it is sometimes necessary to copy a pnode
* (see dirty_cow_pnode). When that happens, references in
* category lists and heaps must be replaced. This function does that.
*/
static void replace_cats(struct ubifs_info *c, struct ubifs_pnode *old_pnode,
struct ubifs_pnode *new_pnode)
{
int i;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
if (!new_pnode->lprops[i].lnum)
return;
ubifs_replace_cat(c, &old_pnode->lprops[i],
&new_pnode->lprops[i]);
}
}
/**
* check_lpt_crc - check LPT node crc is correct.
* @c: UBIFS file-system description object
* @buf: buffer containing node
* @len: length of node
*
* This function returns %0 on success and a negative error code on failure.
*/
static int check_lpt_crc(void *buf, int len)
{
int pos = 0;
uint8_t *addr = buf;
uint16_t crc, calc_crc;
crc = ubifs_unpack_bits(&addr, &pos, UBIFS_LPT_CRC_BITS);
calc_crc = crc16(-1, buf + UBIFS_LPT_CRC_BYTES,
len - UBIFS_LPT_CRC_BYTES);
if (crc != calc_crc) {
ubifs_err("invalid crc in LPT node: crc %hx calc %hx", crc,
calc_crc);
dump_stack();
return -EINVAL;
}
return 0;
}
/**
* check_lpt_type - check LPT node type is correct.
* @c: UBIFS file-system description object
* @addr: address of type bit field is passed and returned updated here
* @pos: position of type bit field is passed and returned updated here
* @type: expected type
*
* This function returns %0 on success and a negative error code on failure.
*/
static int check_lpt_type(uint8_t **addr, int *pos, int type)
{
int node_type;
node_type = ubifs_unpack_bits(addr, pos, UBIFS_LPT_TYPE_BITS);
if (node_type != type) {
ubifs_err("invalid type (%d) in LPT node type %d", node_type,
type);
dump_stack();
return -EINVAL;
}
return 0;
}
/**
* unpack_pnode - unpack a pnode.
* @c: UBIFS file-system description object
* @buf: buffer containing packed pnode to unpack
* @pnode: pnode structure to fill
*
* This function returns %0 on success and a negative error code on failure.
*/
static int unpack_pnode(const struct ubifs_info *c, void *buf,
struct ubifs_pnode *pnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0, err;
err = check_lpt_type(&addr, &pos, UBIFS_LPT_PNODE);
if (err)
return err;
if (c->big_lpt)
pnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
struct ubifs_lprops * const lprops = &pnode->lprops[i];
lprops->free = ubifs_unpack_bits(&addr, &pos, c->space_bits);
lprops->free <<= 3;
lprops->dirty = ubifs_unpack_bits(&addr, &pos, c->space_bits);
lprops->dirty <<= 3;
if (ubifs_unpack_bits(&addr, &pos, 1))
lprops->flags = LPROPS_INDEX;
else
lprops->flags = 0;
lprops->flags |= ubifs_categorize_lprops(c, lprops);
}
err = check_lpt_crc(buf, c->pnode_sz);
return err;
}
/**
* ubifs_unpack_nnode - unpack a nnode.
* @c: UBIFS file-system description object
* @buf: buffer containing packed nnode to unpack
* @nnode: nnode structure to fill
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_unpack_nnode(const struct ubifs_info *c, void *buf,
struct ubifs_nnode *nnode)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0, err;
err = check_lpt_type(&addr, &pos, UBIFS_LPT_NNODE);
if (err)
return err;
if (c->big_lpt)
nnode->num = ubifs_unpack_bits(&addr, &pos, c->pcnt_bits);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int lnum;
lnum = ubifs_unpack_bits(&addr, &pos, c->lpt_lnum_bits) +
c->lpt_first;
if (lnum == c->lpt_last + 1)
lnum = 0;
nnode->nbranch[i].lnum = lnum;
nnode->nbranch[i].offs = ubifs_unpack_bits(&addr, &pos,
c->lpt_offs_bits);
}
err = check_lpt_crc(buf, c->nnode_sz);
return err;
}
/**
* unpack_ltab - unpack the LPT's own lprops table.
* @c: UBIFS file-system description object
* @buf: buffer from which to unpack
*
* This function returns %0 on success and a negative error code on failure.
*/
static int unpack_ltab(const struct ubifs_info *c, void *buf)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0, err;
err = check_lpt_type(&addr, &pos, UBIFS_LPT_LTAB);
if (err)
return err;
for (i = 0; i < c->lpt_lebs; i++) {
int free = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits);
int dirty = ubifs_unpack_bits(&addr, &pos, c->lpt_spc_bits);
if (free < 0 || free > c->leb_size || dirty < 0 ||
dirty > c->leb_size || free + dirty > c->leb_size)
return -EINVAL;
c->ltab[i].free = free;
c->ltab[i].dirty = dirty;
c->ltab[i].tgc = 0;
c->ltab[i].cmt = 0;
}
err = check_lpt_crc(buf, c->ltab_sz);
return err;
}
/**
* unpack_lsave - unpack the LPT's save table.
* @c: UBIFS file-system description object
* @buf: buffer from which to unpack
*
* This function returns %0 on success and a negative error code on failure.
*/
static int unpack_lsave(const struct ubifs_info *c, void *buf)
{
uint8_t *addr = buf + UBIFS_LPT_CRC_BYTES;
int i, pos = 0, err;
err = check_lpt_type(&addr, &pos, UBIFS_LPT_LSAVE);
if (err)
return err;
for (i = 0; i < c->lsave_cnt; i++) {
int lnum = ubifs_unpack_bits(&addr, &pos, c->lnum_bits);
if (lnum < c->main_first || lnum >= c->leb_cnt)
return -EINVAL;
c->lsave[i] = lnum;
}
err = check_lpt_crc(buf, c->lsave_sz);
return err;
}
/**
* validate_nnode - validate a nnode.
* @c: UBIFS file-system description object
* @nnode: nnode to validate
* @parent: parent nnode (or NULL for the root nnode)
* @iip: index in parent
*
* This function returns %0 on success and a negative error code on failure.
*/
static int validate_nnode(const struct ubifs_info *c, struct ubifs_nnode *nnode,
struct ubifs_nnode *parent, int iip)
{
int i, lvl, max_offs;
if (c->big_lpt) {
int num = calc_nnode_num_from_parent(c, parent, iip);
if (nnode->num != num)
return -EINVAL;
}
lvl = parent ? parent->level - 1 : c->lpt_hght;
if (lvl < 1)
return -EINVAL;
if (lvl == 1)
max_offs = c->leb_size - c->pnode_sz;
else
max_offs = c->leb_size - c->nnode_sz;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int lnum = nnode->nbranch[i].lnum;
int offs = nnode->nbranch[i].offs;
if (lnum == 0) {
if (offs != 0)
return -EINVAL;
continue;
}
if (lnum < c->lpt_first || lnum > c->lpt_last)
return -EINVAL;
if (offs < 0 || offs > max_offs)
return -EINVAL;
}
return 0;
}
/**
* validate_pnode - validate a pnode.
* @c: UBIFS file-system description object
* @pnode: pnode to validate
* @parent: parent nnode
* @iip: index in parent
*
* This function returns %0 on success and a negative error code on failure.
*/
static int validate_pnode(const struct ubifs_info *c, struct ubifs_pnode *pnode,
struct ubifs_nnode *parent, int iip)
{
int i;
if (c->big_lpt) {
int num = calc_pnode_num_from_parent(c, parent, iip);
if (pnode->num != num)
return -EINVAL;
}
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
int free = pnode->lprops[i].free;
int dirty = pnode->lprops[i].dirty;
if (free < 0 || free > c->leb_size || free % c->min_io_size ||
(free & 7))
return -EINVAL;
if (dirty < 0 || dirty > c->leb_size || (dirty & 7))
return -EINVAL;
if (dirty + free > c->leb_size)
return -EINVAL;
}
return 0;
}
/**
* set_pnode_lnum - set LEB numbers on a pnode.
* @c: UBIFS file-system description object
* @pnode: pnode to update
*
* This function calculates the LEB numbers for the LEB properties it contains
* based on the pnode number.
*/
static void set_pnode_lnum(const struct ubifs_info *c,
struct ubifs_pnode *pnode)
{
int i, lnum;
lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + c->main_first;
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
if (lnum >= c->leb_cnt)
return;
pnode->lprops[i].lnum = lnum++;
}
}
/**
* ubifs_read_nnode - read a nnode from flash and link it to the tree in memory.
* @c: UBIFS file-system description object
* @parent: parent nnode (or NULL for the root)
* @iip: index in parent
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_read_nnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch = NULL;
struct ubifs_nnode *nnode = NULL;
void *buf = c->lpt_nod_buf;
int err, lnum, offs;
if (parent) {
branch = &parent->nbranch[iip];
lnum = branch->lnum;
offs = branch->offs;
} else {
lnum = c->lpt_lnum;
offs = c->lpt_offs;
}
nnode = kzalloc(sizeof(struct ubifs_nnode), GFP_NOFS);
if (!nnode) {
err = -ENOMEM;
goto out;
}
if (lnum == 0) {
/*
* This nnode was not written which just means that the LEB
* properties in the subtree below it describe empty LEBs. We
* make the nnode as though we had read it, which in fact means
* doing almost nothing.
*/
if (c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->nnode_sz, 1);
if (err)
goto out;
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
goto out;
}
err = validate_nnode(c, nnode, parent, iip);
if (err)
goto out;
if (!c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
if (parent) {
branch->nnode = nnode;
nnode->level = parent->level - 1;
} else {
c->nroot = nnode;
nnode->level = c->lpt_hght;
}
nnode->parent = parent;
nnode->iip = iip;
return 0;
out:
ubifs_err("error %d reading nnode at %d:%d", err, lnum, offs);
dump_stack();
kfree(nnode);
return err;
}
/**
* read_pnode - read a pnode from flash and link it to the tree in memory.
* @c: UBIFS file-system description object
* @parent: parent nnode
* @iip: index in parent
*
* This function returns %0 on success and a negative error code on failure.
*/
static int read_pnode(struct ubifs_info *c, struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch;
struct ubifs_pnode *pnode = NULL;
void *buf = c->lpt_nod_buf;
int err, lnum, offs;
branch = &parent->nbranch[iip];
lnum = branch->lnum;
offs = branch->offs;
pnode = kzalloc(sizeof(struct ubifs_pnode), GFP_NOFS);
if (!pnode)
return -ENOMEM;
if (lnum == 0) {
/*
* This pnode was not written which just means that the LEB
* properties in it describe empty LEBs. We make the pnode as
* though we had read it.
*/
int i;
if (c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
struct ubifs_lprops * const lprops = &pnode->lprops[i];
lprops->free = c->leb_size;
lprops->flags = ubifs_categorize_lprops(c, lprops);
}
} else {
err = ubifs_leb_read(c, lnum, buf, offs, c->pnode_sz, 1);
if (err)
goto out;
err = unpack_pnode(c, buf, pnode);
if (err)
goto out;
}
err = validate_pnode(c, pnode, parent, iip);
if (err)
goto out;
if (!c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip);
branch->pnode = pnode;
pnode->parent = parent;
pnode->iip = iip;
set_pnode_lnum(c, pnode);
c->pnodes_have += 1;
return 0;
out:
ubifs_err("error %d reading pnode at %d:%d", err, lnum, offs);
ubifs_dump_pnode(c, pnode, parent, iip);
dump_stack();
dbg_msg("calc num: %d", calc_pnode_num_from_parent(c, parent, iip));
kfree(pnode);
return err;
}
/**
* read_ltab - read LPT's own lprops table.
* @c: UBIFS file-system description object
*
* This function returns %0 on success and a negative error code on failure.
*/
static int read_ltab(struct ubifs_info *c)
{
int err;
void *buf;
buf = vmalloc(c->ltab_sz);
if (!buf)
return -ENOMEM;
err = ubifs_leb_read(c, c->ltab_lnum, buf, c->ltab_offs, c->ltab_sz, 1);
if (err)
goto out;
err = unpack_ltab(c, buf);
out:
vfree(buf);
return err;
}
/**
* read_lsave - read LPT's save table.
* @c: UBIFS file-system description object
*
* This function returns %0 on success and a negative error code on failure.
*/
static int read_lsave(struct ubifs_info *c)
{
int err, i;
void *buf;
buf = vmalloc(c->lsave_sz);
if (!buf)
return -ENOMEM;
err = ubifs_leb_read(c, c->lsave_lnum, buf, c->lsave_offs,
c->lsave_sz, 1);
if (err)
goto out;
err = unpack_lsave(c, buf);
if (err)
goto out;
for (i = 0; i < c->lsave_cnt; i++) {
int lnum = c->lsave[i];
struct ubifs_lprops *lprops;
/*
* Due to automatic resizing, the values in the lsave table
* could be beyond the volume size - just ignore them.
*/
if (lnum >= c->leb_cnt)
continue;
lprops = ubifs_lpt_lookup(c, lnum);
if (IS_ERR(lprops)) {
err = PTR_ERR(lprops);
goto out;
}
}
out:
vfree(buf);
return err;
}
/**
* ubifs_get_nnode - get a nnode.
* @c: UBIFS file-system description object
* @parent: parent nnode (or NULL for the root)
* @iip: index in parent
*
* This function returns a pointer to the nnode on success or a negative error
* code on failure.
*/
struct ubifs_nnode *ubifs_get_nnode(struct ubifs_info *c,
struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch;
struct ubifs_nnode *nnode;
int err;
branch = &parent->nbranch[iip];
nnode = branch->nnode;
if (nnode)
return nnode;
err = ubifs_read_nnode(c, parent, iip);
if (err)
return ERR_PTR(err);
return branch->nnode;
}
/**
* ubifs_get_pnode - get a pnode.
* @c: UBIFS file-system description object
* @parent: parent nnode
* @iip: index in parent
*
* This function returns a pointer to the pnode on success or a negative error
* code on failure.
*/
struct ubifs_pnode *ubifs_get_pnode(struct ubifs_info *c,
struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch;
struct ubifs_pnode *pnode;
int err;
branch = &parent->nbranch[iip];
pnode = branch->pnode;
if (pnode)
return pnode;
err = read_pnode(c, parent, iip);
if (err)
return ERR_PTR(err);
update_cats(c, branch->pnode);
return branch->pnode;
}
/**
* ubifs_lpt_lookup - lookup LEB properties in the LPT.
* @c: UBIFS file-system description object
* @lnum: LEB number to lookup
*
* This function returns a pointer to the LEB properties on success or a
* negative error code on failure.
*/
struct ubifs_lprops *ubifs_lpt_lookup(struct ubifs_info *c, int lnum)
{
int err, i, h, iip, shft;
struct ubifs_nnode *nnode;
struct ubifs_pnode *pnode;
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return ERR_PTR(err);
}
nnode = c->nroot;
i = lnum - c->main_first;
shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
for (h = 1; h < c->lpt_hght; h++) {
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
nnode = ubifs_get_nnode(c, nnode, iip);
if (IS_ERR(nnode))
return ERR_CAST(nnode);
}
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
pnode = ubifs_get_pnode(c, nnode, iip);
if (IS_ERR(pnode))
return ERR_CAST(pnode);
iip = (i & (UBIFS_LPT_FANOUT - 1));
dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
pnode->lprops[iip].free, pnode->lprops[iip].dirty,
pnode->lprops[iip].flags);
return &pnode->lprops[iip];
}
/**
* dirty_cow_nnode - ensure a nnode is not being committed.
* @c: UBIFS file-system description object
* @nnode: nnode to check
*
* Returns dirtied nnode on success or negative error code on failure.
*/
static struct ubifs_nnode *dirty_cow_nnode(struct ubifs_info *c,
struct ubifs_nnode *nnode)
{
struct ubifs_nnode *n;
int i;
if (!test_bit(COW_CNODE, &nnode->flags)) {
/* nnode is not being committed */
if (!test_and_set_bit(DIRTY_CNODE, &nnode->flags)) {
c->dirty_nn_cnt += 1;
ubifs_add_nnode_dirt(c, nnode);
}
return nnode;
}
/* nnode is being committed, so copy it */
n = kmalloc(sizeof(struct ubifs_nnode), GFP_NOFS);
if (unlikely(!n))
return ERR_PTR(-ENOMEM);
memcpy(n, nnode, sizeof(struct ubifs_nnode));
n->cnext = NULL;
__set_bit(DIRTY_CNODE, &n->flags);
__clear_bit(COW_CNODE, &n->flags);
/* The children now have new parent */
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
struct ubifs_nbranch *branch = &n->nbranch[i];
if (branch->cnode)
branch->cnode->parent = n;
}
ubifs_assert(!test_bit(OBSOLETE_CNODE, &nnode->flags));
__set_bit(OBSOLETE_CNODE, &nnode->flags);
c->dirty_nn_cnt += 1;
ubifs_add_nnode_dirt(c, nnode);
if (nnode->parent)
nnode->parent->nbranch[n->iip].nnode = n;
else
c->nroot = n;
return n;
}
/**
* dirty_cow_pnode - ensure a pnode is not being committed.
* @c: UBIFS file-system description object
* @pnode: pnode to check
*
* Returns dirtied pnode on success or negative error code on failure.
*/
static struct ubifs_pnode *dirty_cow_pnode(struct ubifs_info *c,
struct ubifs_pnode *pnode)
{
struct ubifs_pnode *p;
if (!test_bit(COW_CNODE, &pnode->flags)) {
/* pnode is not being committed */
if (!test_and_set_bit(DIRTY_CNODE, &pnode->flags)) {
c->dirty_pn_cnt += 1;
add_pnode_dirt(c, pnode);
}
return pnode;
}
/* pnode is being committed, so copy it */
p = kmalloc(sizeof(struct ubifs_pnode), GFP_NOFS);
if (unlikely(!p))
return ERR_PTR(-ENOMEM);
memcpy(p, pnode, sizeof(struct ubifs_pnode));
p->cnext = NULL;
__set_bit(DIRTY_CNODE, &p->flags);
__clear_bit(COW_CNODE, &p->flags);
replace_cats(c, pnode, p);
ubifs_assert(!test_bit(OBSOLETE_CNODE, &pnode->flags));
__set_bit(OBSOLETE_CNODE, &pnode->flags);
c->dirty_pn_cnt += 1;
add_pnode_dirt(c, pnode);
pnode->parent->nbranch[p->iip].pnode = p;
return p;
}
/**
* ubifs_lpt_lookup_dirty - lookup LEB properties in the LPT.
* @c: UBIFS file-system description object
* @lnum: LEB number to lookup
*
* This function returns a pointer to the LEB properties on success or a
* negative error code on failure.
*/
struct ubifs_lprops *ubifs_lpt_lookup_dirty(struct ubifs_info *c, int lnum)
{
int err, i, h, iip, shft;
struct ubifs_nnode *nnode;
struct ubifs_pnode *pnode;
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return ERR_PTR(err);
}
nnode = c->nroot;
nnode = dirty_cow_nnode(c, nnode);
if (IS_ERR(nnode))
return ERR_CAST(nnode);
i = lnum - c->main_first;
shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
for (h = 1; h < c->lpt_hght; h++) {
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
nnode = ubifs_get_nnode(c, nnode, iip);
if (IS_ERR(nnode))
return ERR_CAST(nnode);
nnode = dirty_cow_nnode(c, nnode);
if (IS_ERR(nnode))
return ERR_CAST(nnode);
}
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
pnode = ubifs_get_pnode(c, nnode, iip);
if (IS_ERR(pnode))
return ERR_CAST(pnode);
pnode = dirty_cow_pnode(c, pnode);
if (IS_ERR(pnode))
return ERR_CAST(pnode);
iip = (i & (UBIFS_LPT_FANOUT - 1));
dbg_lp("LEB %d, free %d, dirty %d, flags %d", lnum,
pnode->lprops[iip].free, pnode->lprops[iip].dirty,
pnode->lprops[iip].flags);
ubifs_assert(test_bit(DIRTY_CNODE, &pnode->flags));
return &pnode->lprops[iip];
}
/**
* lpt_init_rd - initialize the LPT for reading.
* @c: UBIFS file-system description object
*
* This function returns %0 on success and a negative error code on failure.
*/
static int lpt_init_rd(struct ubifs_info *c)
{
int err, i;
c->ltab = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
if (!c->ltab)
return -ENOMEM;
i = max_t(int, c->nnode_sz, c->pnode_sz);
c->lpt_nod_buf = kmalloc(i, GFP_KERNEL);
if (!c->lpt_nod_buf)
return -ENOMEM;
for (i = 0; i < LPROPS_HEAP_CNT; i++) {
c->lpt_heap[i].arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ,
GFP_KERNEL);
if (!c->lpt_heap[i].arr)
return -ENOMEM;
c->lpt_heap[i].cnt = 0;
c->lpt_heap[i].max_cnt = LPT_HEAP_SZ;
}
c->dirty_idx.arr = kmalloc(sizeof(void *) * LPT_HEAP_SZ, GFP_KERNEL);
if (!c->dirty_idx.arr)
return -ENOMEM;
c->dirty_idx.cnt = 0;
c->dirty_idx.max_cnt = LPT_HEAP_SZ;
err = read_ltab(c);
if (err)
return err;
dbg_lp("space_bits %d", c->space_bits);
dbg_lp("lpt_lnum_bits %d", c->lpt_lnum_bits);
dbg_lp("lpt_offs_bits %d", c->lpt_offs_bits);
dbg_lp("lpt_spc_bits %d", c->lpt_spc_bits);
dbg_lp("pcnt_bits %d", c->pcnt_bits);
dbg_lp("lnum_bits %d", c->lnum_bits);
dbg_lp("pnode_sz %d", c->pnode_sz);
dbg_lp("nnode_sz %d", c->nnode_sz);
dbg_lp("ltab_sz %d", c->ltab_sz);
dbg_lp("lsave_sz %d", c->lsave_sz);
dbg_lp("lsave_cnt %d", c->lsave_cnt);
dbg_lp("lpt_hght %d", c->lpt_hght);
dbg_lp("big_lpt %d", c->big_lpt);
dbg_lp("LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
dbg_lp("LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
dbg_lp("LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt)
dbg_lp("LPT lsave is at %d:%d", c->lsave_lnum, c->lsave_offs);
return 0;
}
/**
* lpt_init_wr - initialize the LPT for writing.
* @c: UBIFS file-system description object
*
* 'lpt_init_rd()' must have been called already.
*
* This function returns %0 on success and a negative error code on failure.
*/
static int lpt_init_wr(struct ubifs_info *c)
{
int err, i;
c->ltab_cmt = vmalloc(sizeof(struct ubifs_lpt_lprops) * c->lpt_lebs);
if (!c->ltab_cmt)
return -ENOMEM;
c->lpt_buf = vmalloc(c->leb_size);
if (!c->lpt_buf)
return -ENOMEM;
if (c->big_lpt) {
c->lsave = kmalloc(sizeof(int) * c->lsave_cnt, GFP_NOFS);
if (!c->lsave)
return -ENOMEM;
err = read_lsave(c);
if (err)
return err;
}
for (i = 0; i < c->lpt_lebs; i++)
if (c->ltab[i].free == c->leb_size) {
err = ubifs_leb_unmap(c, i + c->lpt_first);
if (err)
return err;
}
return 0;
}
/**
* ubifs_lpt_init - initialize the LPT.
* @c: UBIFS file-system description object
* @rd: whether to initialize lpt for reading
* @wr: whether to initialize lpt for writing
*
* For mounting 'rw', @rd and @wr are both true. For mounting 'ro', @rd is true
* and @wr is false. For mounting from 'ro' to 'rw', @rd is false and @wr is
* true.
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_lpt_init(struct ubifs_info *c, int rd, int wr)
{
int err;
if (rd) {
err = lpt_init_rd(c);
if (err)
goto out_err;
}
if (wr) {
err = lpt_init_wr(c);
if (err)
goto out_err;
}
return 0;
out_err:
ubifs_lpt_free(c, 0);
return err;
}
/**
* struct lpt_scan_node - somewhere to put nodes while we scan LPT.
* @nnode: where to keep a nnode
* @pnode: where to keep a pnode
* @cnode: where to keep a cnode
* @in_tree: is the node in the tree in memory
* @ptr.nnode: pointer to the nnode (if it is an nnode) which may be here or in
* the tree
* @ptr.pnode: ditto for pnode
* @ptr.cnode: ditto for cnode
*/
struct lpt_scan_node {
union {
struct ubifs_nnode nnode;
struct ubifs_pnode pnode;
struct ubifs_cnode cnode;
};
int in_tree;
union {
struct ubifs_nnode *nnode;
struct ubifs_pnode *pnode;
struct ubifs_cnode *cnode;
} ptr;
};
/**
* scan_get_nnode - for the scan, get a nnode from either the tree or flash.
* @c: the UBIFS file-system description object
* @path: where to put the nnode
* @parent: parent of the nnode
* @iip: index in parent of the nnode
*
* This function returns a pointer to the nnode on success or a negative error
* code on failure.
*/
static struct ubifs_nnode *scan_get_nnode(struct ubifs_info *c,
struct lpt_scan_node *path,
struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch;
struct ubifs_nnode *nnode;
void *buf = c->lpt_nod_buf;
int err;
branch = &parent->nbranch[iip];
nnode = branch->nnode;
if (nnode) {
path->in_tree = 1;
path->ptr.nnode = nnode;
return nnode;
}
nnode = &path->nnode;
path->in_tree = 0;
path->ptr.nnode = nnode;
memset(nnode, 0, sizeof(struct ubifs_nnode));
if (branch->lnum == 0) {
/*
* This nnode was not written which just means that the LEB
* properties in the subtree below it describe empty LEBs. We
* make the nnode as though we had read it, which in fact means
* doing almost nothing.
*/
if (c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
} else {
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->nnode_sz, 1);
if (err)
return ERR_PTR(err);
err = ubifs_unpack_nnode(c, buf, nnode);
if (err)
return ERR_PTR(err);
}
err = validate_nnode(c, nnode, parent, iip);
if (err)
return ERR_PTR(err);
if (!c->big_lpt)
nnode->num = calc_nnode_num_from_parent(c, parent, iip);
nnode->level = parent->level - 1;
nnode->parent = parent;
nnode->iip = iip;
return nnode;
}
/**
* scan_get_pnode - for the scan, get a pnode from either the tree or flash.
* @c: the UBIFS file-system description object
* @path: where to put the pnode
* @parent: parent of the pnode
* @iip: index in parent of the pnode
*
* This function returns a pointer to the pnode on success or a negative error
* code on failure.
*/
static struct ubifs_pnode *scan_get_pnode(struct ubifs_info *c,
struct lpt_scan_node *path,
struct ubifs_nnode *parent, int iip)
{
struct ubifs_nbranch *branch;
struct ubifs_pnode *pnode;
void *buf = c->lpt_nod_buf;
int err;
branch = &parent->nbranch[iip];
pnode = branch->pnode;
if (pnode) {
path->in_tree = 1;
path->ptr.pnode = pnode;
return pnode;
}
pnode = &path->pnode;
path->in_tree = 0;
path->ptr.pnode = pnode;
memset(pnode, 0, sizeof(struct ubifs_pnode));
if (branch->lnum == 0) {
/*
* This pnode was not written which just means that the LEB
* properties in it describe empty LEBs. We make the pnode as
* though we had read it.
*/
int i;
if (c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip);
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
struct ubifs_lprops * const lprops = &pnode->lprops[i];
lprops->free = c->leb_size;
lprops->flags = ubifs_categorize_lprops(c, lprops);
}
} else {
ubifs_assert(branch->lnum >= c->lpt_first &&
branch->lnum <= c->lpt_last);
ubifs_assert(branch->offs >= 0 && branch->offs < c->leb_size);
err = ubifs_leb_read(c, branch->lnum, buf, branch->offs,
c->pnode_sz, 1);
if (err)
return ERR_PTR(err);
err = unpack_pnode(c, buf, pnode);
if (err)
return ERR_PTR(err);
}
err = validate_pnode(c, pnode, parent, iip);
if (err)
return ERR_PTR(err);
if (!c->big_lpt)
pnode->num = calc_pnode_num_from_parent(c, parent, iip);
pnode->parent = parent;
pnode->iip = iip;
set_pnode_lnum(c, pnode);
return pnode;
}
/**
* ubifs_lpt_scan_nolock - scan the LPT.
* @c: the UBIFS file-system description object
* @start_lnum: LEB number from which to start scanning
* @end_lnum: LEB number at which to stop scanning
* @scan_cb: callback function called for each lprops
* @data: data to be passed to the callback function
*
* This function returns %0 on success and a negative error code on failure.
*/
int ubifs_lpt_scan_nolock(struct ubifs_info *c, int start_lnum, int end_lnum,
ubifs_lpt_scan_callback scan_cb, void *data)
{
int err = 0, i, h, iip, shft;
struct ubifs_nnode *nnode;
struct ubifs_pnode *pnode;
struct lpt_scan_node *path;
if (start_lnum == -1) {
start_lnum = end_lnum + 1;
if (start_lnum >= c->leb_cnt)
start_lnum = c->main_first;
}
ubifs_assert(start_lnum >= c->main_first && start_lnum < c->leb_cnt);
ubifs_assert(end_lnum >= c->main_first && end_lnum < c->leb_cnt);
if (!c->nroot) {
err = ubifs_read_nnode(c, NULL, 0);
if (err)
return err;
}
path = kmalloc(sizeof(struct lpt_scan_node) * (c->lpt_hght + 1),
GFP_NOFS);
if (!path)
return -ENOMEM;
path[0].ptr.nnode = c->nroot;
path[0].in_tree = 1;
again:
/* Descend to the pnode containing start_lnum */
nnode = c->nroot;
i = start_lnum - c->main_first;
shft = c->lpt_hght * UBIFS_LPT_FANOUT_SHIFT;
for (h = 1; h < c->lpt_hght; h++) {
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
nnode = scan_get_nnode(c, path + h, nnode, iip);
if (IS_ERR(nnode)) {
err = PTR_ERR(nnode);
goto out;
}
}
iip = ((i >> shft) & (UBIFS_LPT_FANOUT - 1));
shft -= UBIFS_LPT_FANOUT_SHIFT;
pnode = scan_get_pnode(c, path + h, nnode, iip);
if (IS_ERR(pnode)) {
err = PTR_ERR(pnode);
goto out;
}
iip = (i & (UBIFS_LPT_FANOUT - 1));
/* Loop for each lprops */
while (1) {
struct ubifs_lprops *lprops = &pnode->lprops[iip];
int ret, lnum = lprops->lnum;
ret = scan_cb(c, lprops, path[h].in_tree, data);
if (ret < 0) {
err = ret;
goto out;
}
if (ret & LPT_SCAN_ADD) {
/* Add all the nodes in path to the tree in memory */
for (h = 1; h < c->lpt_hght; h++) {
const size_t sz = sizeof(struct ubifs_nnode);
struct ubifs_nnode *parent;
if (path[h].in_tree)
continue;
nnode = kmemdup(&path[h].nnode, sz, GFP_NOFS);
if (!nnode) {
err = -ENOMEM;
goto out;
}
parent = nnode->parent;
parent->nbranch[nnode->iip].nnode = nnode;
path[h].ptr.nnode = nnode;
path[h].in_tree = 1;
path[h + 1].cnode.parent = nnode;
}
if (path[h].in_tree)
ubifs_ensure_cat(c, lprops);
else {
const size_t sz = sizeof(struct ubifs_pnode);
struct ubifs_nnode *parent;
pnode = kmemdup(&path[h].pnode, sz, GFP_NOFS);
if (!pnode) {
err = -ENOMEM;
goto out;
}
parent = pnode->parent;
parent->nbranch[pnode->iip].pnode = pnode;
path[h].ptr.pnode = pnode;
path[h].in_tree = 1;
update_cats(c, pnode);
c->pnodes_have += 1;
}
err = dbg_check_lpt_nodes(c, (struct ubifs_cnode *)
c->nroot, 0, 0);
if (err)
goto out;
err = dbg_check_cats(c);
if (err)
goto out;
}
if (ret & LPT_SCAN_STOP) {
err = 0;
break;
}
/* Get the next lprops */
if (lnum == end_lnum) {
/*
* We got to the end without finding what we were
* looking for
*/
err = -ENOSPC;
goto out;
}
if (lnum + 1 >= c->leb_cnt) {
/* Wrap-around to the beginning */
start_lnum = c->main_first;
goto again;
}
if (iip + 1 < UBIFS_LPT_FANOUT) {
/* Next lprops is in the same pnode */
iip += 1;
continue;
}
/* We need to get the next pnode. Go up until we can go right */
iip = pnode->iip;
while (1) {
h -= 1;
ubifs_assert(h >= 0);
nnode = path[h].ptr.nnode;
if (iip + 1 < UBIFS_LPT_FANOUT)
break;
iip = nnode->iip;
}
/* Go right */
iip += 1;
/* Descend to the pnode */
h += 1;
for (; h < c->lpt_hght; h++) {
nnode = scan_get_nnode(c, path + h, nnode, iip);
if (IS_ERR(nnode)) {
err = PTR_ERR(nnode);
goto out;
}
iip = 0;
}
pnode = scan_get_pnode(c, path + h, nnode, iip);
if (IS_ERR(pnode)) {
err = PTR_ERR(pnode);
goto out;
}
iip = 0;
}
out:
kfree(path);
return err;
}
/**
* dbg_chk_pnode - check a pnode.
* @c: the UBIFS file-system description object
* @pnode: pnode to check
* @col: pnode column
*
* This function returns %0 on success and a negative error code on failure.
*/
static int dbg_chk_pnode(struct ubifs_info *c, struct ubifs_pnode *pnode,
int col)
{
int i;
if (pnode->num != col) {
ubifs_err("pnode num %d expected %d parent num %d iip %d",
pnode->num, col, pnode->parent->num, pnode->iip);
return -EINVAL;
}
for (i = 0; i < UBIFS_LPT_FANOUT; i++) {
struct ubifs_lprops *lp, *lprops = &pnode->lprops[i];
int lnum = (pnode->num << UBIFS_LPT_FANOUT_SHIFT) + i +
c->main_first;
int found, cat = lprops->flags & LPROPS_CAT_MASK;
struct ubifs_lpt_heap *heap;
struct list_head *list = NULL;
if (lnum >= c->leb_cnt)
continue;
if (lprops->lnum != lnum) {
ubifs_err("bad LEB number %d expected %d",
lprops->lnum, lnum);
return -EINVAL;
}
if (lprops->flags & LPROPS_TAKEN) {
if (cat != LPROPS_UNCAT) {
ubifs_err("LEB %d taken but not uncat %d",
lprops->lnum, cat);
return -EINVAL;
}
continue;
}
if (lprops->flags & LPROPS_INDEX) {
switch (cat) {
case LPROPS_UNCAT:
case LPROPS_DIRTY_IDX:
case LPROPS_FRDI_IDX:
break;
default:
ubifs_err("LEB %d index but cat %d",
lprops->lnum, cat);
return -EINVAL;
}
} else {
switch (cat) {
case LPROPS_UNCAT:
case LPROPS_DIRTY:
case LPROPS_FREE:
case LPROPS_EMPTY:
case LPROPS_FREEABLE:
break;
default:
ubifs_err("LEB %d not index but cat %d",
lprops->lnum, cat);
return -EINVAL;
}
}
switch (cat) {
case LPROPS_UNCAT:
list = &c->uncat_list;
break;
case LPROPS_EMPTY:
list = &c->empty_list;
break;
case LPROPS_FREEABLE:
list = &c->freeable_list;
break;
case LPROPS_FRDI_IDX:
list = &c->frdi_idx_list;
break;
}
found = 0;
switch (cat) {
case LPROPS_DIRTY:
case LPROPS_DIRTY_IDX:
case LPROPS_FREE:
heap = &c->lpt_heap[cat - 1];
if (lprops->hpos < heap->cnt &&
heap->arr[lprops->hpos] == lprops)
found = 1;
break;
case LPROPS_UNCAT:
case LPROPS_EMPTY:
case LPROPS_FREEABLE:
case LPROPS_FRDI_IDX:
list_for_each_entry(lp, list, list)
if (lprops == lp) {
found = 1;
break;
}
break;
}
if (!found) {
ubifs_err("LEB %d cat %d not found in cat heap/list",
lprops->lnum, cat);
return -EINVAL;
}
switch (cat) {
case LPROPS_EMPTY:
if (lprops->free != c->leb_size) {
ubifs_err("LEB %d cat %d free %d dirty %d",
lprops->lnum, cat, lprops->free,
lprops->dirty);
return -EINVAL;
}
case LPROPS_FREEABLE:
case LPROPS_FRDI_IDX:
if (lprops->free + lprops->dirty != c->leb_size) {
ubifs_err("LEB %d cat %d free %d dirty %d",
lprops->lnum, cat, lprops->free,
lprops->dirty);
return -EINVAL;
}
}
}
return 0;
}
/**
* dbg_check_lpt_nodes - check nnodes and pnodes.
* @c: the UBIFS file-system description object
* @cnode: next cnode (nnode or pnode) to check
* @row: row of cnode (root is zero)
* @col: column of cnode (leftmost is zero)
*
* This function returns %0 on success and a negative error code on failure.
*/
int dbg_check_lpt_nodes(struct ubifs_info *c, struct ubifs_cnode *cnode,
int row, int col)
{
struct ubifs_nnode *nnode, *nn;
struct ubifs_cnode *cn;
int num, iip = 0, err;
if (!dbg_is_chk_lprops(c))
return 0;
while (cnode) {
ubifs_assert(row >= 0);
nnode = cnode->parent;
if (cnode->level) {
/* cnode is a nnode */
num = calc_nnode_num(row, col);
if (cnode->num != num) {
ubifs_err("nnode num %d expected %d "
"parent num %d iip %d",
cnode->num, num,
(nnode ? nnode->num : 0), cnode->iip);
return -EINVAL;
}
nn = (struct ubifs_nnode *)cnode;
while (iip < UBIFS_LPT_FANOUT) {
cn = nn->nbranch[iip].cnode;
if (cn) {
/* Go down */
row += 1;
col <<= UBIFS_LPT_FANOUT_SHIFT;
col += iip;
iip = 0;
cnode = cn;
break;
}
/* Go right */
iip += 1;
}
if (iip < UBIFS_LPT_FANOUT)
continue;
} else {
struct ubifs_pnode *pnode;
/* cnode is a pnode */
pnode = (struct ubifs_pnode *)cnode;
err = dbg_chk_pnode(c, pnode, col);
if (err)
return err;
}
/* Go up and to the right */
row -= 1;
col >>= UBIFS_LPT_FANOUT_SHIFT;
iip = cnode->iip + 1;
cnode = (struct ubifs_cnode *)nnode;
}
return 0;
}