blob: f6d43522eb3632b8e1d763d1ee18187dce8e1dcd [file] [log] [blame]
/*
* This file is part of UBIFS.
*
* Copyright (C) 2006, 2007 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
*/
#include "mkfs.ubifs.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 n, bits, per_leb_wastage;
long long sz, tot_wastage;
c->pnode_cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
n = (c->pnode_cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
c->nnode_cnt = n;
while (n > 1) {
n = (n + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
c->nnode_cnt += n;
}
c->lpt_hght = 1;
n = UBIFS_LPT_FANOUT;
while (n < c->pnode_cnt) {
c->lpt_hght += 1;
n <<= UBIFS_LPT_FANOUT_SHIFT;
}
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 = (c->max_leb_cnt + UBIFS_LPT_FANOUT - 1) / 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;
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;
}
/**
* 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.
*/
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 */
sz += c->leb_size - 1;
do_div(sz, c->leb_size);
lebs_needed = sz;
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) {
err_msg("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;
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;
}
/**
* 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
*/
static void 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);
}
/**
* 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
*/
static void 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);
}
/**
* 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
*/
static void 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);
}
/**
* 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
*/
static void 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);
}
/**
* 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_msg(3, "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);
c->ltab[lnum - c->lpt_first].free = free;
c->ltab[lnum - c->lpt_first].dirty = dirty;
}
/**
* 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;
}
/**
* create_lpt - create LPT.
* @c: UBIFS file-system description object
*
* This function returns %0 on success and a negative error code on failure.
*/
int create_lpt(struct ubifs_info *c)
{
int lnum, err = 0, 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;
int *lsave = NULL;
pnode = malloc(sizeof(struct ubifs_pnode));
nnode = malloc(sizeof(struct ubifs_nnode));
buf = malloc(c->leb_size);
lsave = malloc(sizeof(int) * c->lsave_cnt);
if (!pnode || !nnode || !buf || !lsave) {
err = -ENOMEM;
goto out;
}
memset(pnode, 0 , sizeof(struct ubifs_pnode));
memset(nnode, 0 , sizeof(struct ubifs_pnode));
c->lscan_lnum = c->main_first;
lnum = c->lpt_first;
p = buf;
len = 0;
/* Number of leaf nodes (pnodes) */
cnt = (c->main_lebs + UBIFS_LPT_FANOUT - 1) >> UBIFS_LPT_FANOUT_SHIFT;
//printf("pnode_cnt=%d\n",cnt);
/*
* 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 pnodes */
for (i = 0; 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 = write_leb(lnum++, alen, buf);
if (err)
goto out;
p = buf;
len = 0;
}
/* Fill in the pnode */
for (j = 0; j < UBIFS_LPT_FANOUT; j++) {
int k = (i << UBIFS_LPT_FANOUT_SHIFT) + j;
if (k < c->main_lebs)
pnode->lprops[j] = c->lpt[k];
else {
pnode->lprops[j].free = c->leb_size;
pnode->lprops[j].dirty = 0;
pnode->lprops[j].flags = 0;
}
}
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 = c->lpt_hght - 1;
/* Add all nnodes, one level at a time */
while (1) {
/* Number of internal nodes (nnodes) at next level */
cnt = (cnt + UBIFS_LPT_FANOUT - 1) / UBIFS_LPT_FANOUT;
if (cnt == 0)
cnt = 1;
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 = write_leb(lnum++, alen, buf);
if (err)
goto out;
p = buf;
len = 0;
}
/* The root is on row zero */
if (row == 0) {
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);
pack_nnode(c, p, nnode);
p += c->nnode_sz;
len += c->nnode_sz;
}
/* Row zero is the top row */
if (row == 0)
break;
/* Update the information about the level below */
bcnt = cnt;
bsz = c->nnode_sz;
row -= 1;
}
if (c->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 = write_leb(lnum++, alen, buf);
if (err)
goto out;
p = buf;
len = 0;
}
c->lsave_lnum = lnum;
c->lsave_offs = len;
for (i = 0; i < c->lsave_cnt; i++)
lsave[i] = c->main_first + i;
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 = write_leb(lnum++, alen, buf);
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);
pack_ltab(c, p, c->ltab);
p += c->ltab_sz;
/* Write remaining buffer */
memset(p, 0xff, alen - len);
err = write_leb(lnum, alen, buf);
if (err)
goto out;
c->nhead_lnum = lnum;
c->nhead_offs = ALIGN(len, c->min_io_size);
dbg_msg(1, "lpt_sz: %lld", c->lpt_sz);
dbg_msg(1, "space_bits: %d", c->space_bits);
dbg_msg(1, "lpt_lnum_bits: %d", c->lpt_lnum_bits);
dbg_msg(1, "lpt_offs_bits: %d", c->lpt_offs_bits);
dbg_msg(1, "lpt_spc_bits: %d", c->lpt_spc_bits);
dbg_msg(1, "pcnt_bits: %d", c->pcnt_bits);
dbg_msg(1, "lnum_bits: %d", c->lnum_bits);
dbg_msg(1, "pnode_sz: %d", c->pnode_sz);
dbg_msg(1, "nnode_sz: %d", c->nnode_sz);
dbg_msg(1, "ltab_sz: %d", c->ltab_sz);
dbg_msg(1, "lsave_sz: %d", c->lsave_sz);
dbg_msg(1, "lsave_cnt: %d", c->lsave_cnt);
dbg_msg(1, "lpt_hght: %d", c->lpt_hght);
dbg_msg(1, "big_lpt: %d", c->big_lpt);
dbg_msg(1, "LPT root is at %d:%d", c->lpt_lnum, c->lpt_offs);
dbg_msg(1, "LPT head is at %d:%d", c->nhead_lnum, c->nhead_offs);
dbg_msg(1, "LPT ltab is at %d:%d", c->ltab_lnum, c->ltab_offs);
if (c->big_lpt)
dbg_msg(1, "LPT lsave is at %d:%d",
c->lsave_lnum, c->lsave_offs);
out:
free(lsave);
free(buf);
free(nnode);
free(pnode);
return err;
}