UBIFS: simplify make_free_space
The 'make_free_space()' function was too complex and this patch simplifies it. It also fixes a bug - the freespace test failed straight away on UBI volumes with 512 bytes LEB size. Signed-off-by: Artem Bityutskiy <Artem.Bityutskiy@nokia.com>
This commit is contained in:
parent
2edc2025c2
commit
2acf806758
|
@ -37,13 +37,10 @@
|
||||||
/*
|
/*
|
||||||
* When pessimistic budget calculations say that there is no enough space,
|
* When pessimistic budget calculations say that there is no enough space,
|
||||||
* UBIFS starts writing back dirty inodes and pages, doing garbage collection,
|
* UBIFS starts writing back dirty inodes and pages, doing garbage collection,
|
||||||
* or committing. The below constants define maximum number of times UBIFS
|
* or committing. The below constant defines maximum number of times UBIFS
|
||||||
* repeats the operations.
|
* repeats the operations.
|
||||||
*/
|
*/
|
||||||
#define MAX_SHRINK_RETRIES 8
|
#define MAX_MKSPC_RETRIES 3
|
||||||
#define MAX_GC_RETRIES 4
|
|
||||||
#define MAX_CMT_RETRIES 2
|
|
||||||
#define MAX_NOSPC_RETRIES 1
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The below constant defines amount of dirty pages which should be written
|
* The below constant defines amount of dirty pages which should be written
|
||||||
|
@ -51,30 +48,6 @@
|
||||||
*/
|
*/
|
||||||
#define NR_TO_WRITE 16
|
#define NR_TO_WRITE 16
|
||||||
|
|
||||||
/**
|
|
||||||
* struct retries_info - information about re-tries while making free space.
|
|
||||||
* @prev_liability: previous liability
|
|
||||||
* @shrink_cnt: how many times the liability was shrinked
|
|
||||||
* @shrink_retries: count of liability shrink re-tries (increased when
|
|
||||||
* liability does not shrink)
|
|
||||||
* @try_gc: GC should be tried first
|
|
||||||
* @gc_retries: how many times GC was run
|
|
||||||
* @cmt_retries: how many times commit has been done
|
|
||||||
* @nospc_retries: how many times GC returned %-ENOSPC
|
|
||||||
*
|
|
||||||
* Since we consider budgeting to be the fast-path, and this structure has to
|
|
||||||
* be allocated on stack and zeroed out, we make it smaller using bit-fields.
|
|
||||||
*/
|
|
||||||
struct retries_info {
|
|
||||||
long long prev_liability;
|
|
||||||
unsigned int shrink_cnt;
|
|
||||||
unsigned int shrink_retries:5;
|
|
||||||
unsigned int try_gc:1;
|
|
||||||
unsigned int gc_retries:4;
|
|
||||||
unsigned int cmt_retries:3;
|
|
||||||
unsigned int nospc_retries:1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* shrink_liability - write-back some dirty pages/inodes.
|
* shrink_liability - write-back some dirty pages/inodes.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
|
@ -146,10 +119,26 @@ static int run_gc(struct ubifs_info *c)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_liability - calculate current liability.
|
||||||
|
* @c: UBIFS file-system description object
|
||||||
|
*
|
||||||
|
* This function calculates and returns current UBIFS liability, i.e. the
|
||||||
|
* amount of bytes UBIFS has "promised" to write to the media.
|
||||||
|
*/
|
||||||
|
static long long get_liability(struct ubifs_info *c)
|
||||||
|
{
|
||||||
|
long long liab;
|
||||||
|
|
||||||
|
spin_lock(&c->space_lock);
|
||||||
|
liab = c->budg_idx_growth + c->budg_data_growth + c->budg_dd_growth;
|
||||||
|
spin_unlock(&c->space_lock);
|
||||||
|
return liab;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* make_free_space - make more free space on the file-system.
|
* make_free_space - make more free space on the file-system.
|
||||||
* @c: UBIFS file-system description object
|
* @c: UBIFS file-system description object
|
||||||
* @ri: information about previous invocations of this function
|
|
||||||
*
|
*
|
||||||
* This function is called when an operation cannot be budgeted because there
|
* This function is called when an operation cannot be budgeted because there
|
||||||
* is supposedly no free space. But in most cases there is some free space:
|
* is supposedly no free space. But in most cases there is some free space:
|
||||||
|
@ -165,87 +154,42 @@ static int run_gc(struct ubifs_info *c)
|
||||||
* Returns %-ENOSPC if it couldn't do more free space, and other negative error
|
* Returns %-ENOSPC if it couldn't do more free space, and other negative error
|
||||||
* codes on failures.
|
* codes on failures.
|
||||||
*/
|
*/
|
||||||
static int make_free_space(struct ubifs_info *c, struct retries_info *ri)
|
static int make_free_space(struct ubifs_info *c)
|
||||||
{
|
{
|
||||||
int err;
|
int err, retries = 0;
|
||||||
|
long long liab1, liab2;
|
||||||
|
|
||||||
/*
|
do {
|
||||||
* If we have some dirty pages and inodes (liability), try to write
|
liab1 = get_liability(c);
|
||||||
* them back unless this was tried too many times without effect
|
/*
|
||||||
* already.
|
* We probably have some dirty pages or inodes (liability), try
|
||||||
*/
|
* to write them back.
|
||||||
if (ri->shrink_retries < MAX_SHRINK_RETRIES && !ri->try_gc) {
|
*/
|
||||||
long long liability;
|
dbg_budg("liability %lld, run write-back", liab1);
|
||||||
|
shrink_liability(c, NR_TO_WRITE);
|
||||||
|
|
||||||
spin_lock(&c->space_lock);
|
liab2 = get_liability(c);
|
||||||
liability = c->budg_idx_growth + c->budg_data_growth +
|
if (liab2 < liab1)
|
||||||
c->budg_dd_growth;
|
return -EAGAIN;
|
||||||
spin_unlock(&c->space_lock);
|
|
||||||
|
|
||||||
if (ri->prev_liability >= liability) {
|
dbg_budg("new liability %lld (not shrinked)", liab2);
|
||||||
/* Liability does not shrink, next time try GC then */
|
|
||||||
ri->shrink_retries += 1;
|
|
||||||
if (ri->gc_retries < MAX_GC_RETRIES)
|
|
||||||
ri->try_gc = 1;
|
|
||||||
dbg_budg("liability did not shrink: retries %d of %d",
|
|
||||||
ri->shrink_retries, MAX_SHRINK_RETRIES);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg_budg("force write-back (count %d)", ri->shrink_cnt);
|
/* Liability did not shrink again, try GC */
|
||||||
shrink_liability(c, NR_TO_WRITE + ri->shrink_cnt);
|
dbg_budg("Run GC");
|
||||||
|
|
||||||
ri->prev_liability = liability;
|
|
||||||
ri->shrink_cnt += 1;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Try to run garbage collector unless it was already tried too many
|
|
||||||
* times.
|
|
||||||
*/
|
|
||||||
if (ri->gc_retries < MAX_GC_RETRIES) {
|
|
||||||
ri->gc_retries += 1;
|
|
||||||
dbg_budg("run GC, retries %d of %d",
|
|
||||||
ri->gc_retries, MAX_GC_RETRIES);
|
|
||||||
|
|
||||||
ri->try_gc = 0;
|
|
||||||
err = run_gc(c);
|
err = run_gc(c);
|
||||||
if (!err)
|
if (!err)
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
if (err == -EAGAIN) {
|
if (err != -EAGAIN && err != -ENOSPC)
|
||||||
dbg_budg("GC asked to commit");
|
/* Some real error happened */
|
||||||
err = ubifs_run_commit(c);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
return -EAGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (err != -ENOSPC)
|
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
/*
|
dbg_budg("Run commit (retries %d)", retries);
|
||||||
* GC could not make any progress. If this is the first time,
|
|
||||||
* then it makes sense to try to commit, because it might make
|
|
||||||
* some dirty space.
|
|
||||||
*/
|
|
||||||
dbg_budg("GC returned -ENOSPC, retries %d",
|
|
||||||
ri->nospc_retries);
|
|
||||||
if (ri->nospc_retries >= MAX_NOSPC_RETRIES)
|
|
||||||
return err;
|
|
||||||
ri->nospc_retries += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Neither GC nor write-back helped, try to commit */
|
|
||||||
if (ri->cmt_retries < MAX_CMT_RETRIES) {
|
|
||||||
ri->cmt_retries += 1;
|
|
||||||
dbg_budg("run commit, retries %d of %d",
|
|
||||||
ri->cmt_retries, MAX_CMT_RETRIES);
|
|
||||||
err = ubifs_run_commit(c);
|
err = ubifs_run_commit(c);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
return -EAGAIN;
|
} while (retries++ < MAX_MKSPC_RETRIES);
|
||||||
}
|
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -523,8 +467,7 @@ static int calc_dd_growth(const struct ubifs_info *c,
|
||||||
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
|
int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||||
{
|
{
|
||||||
int uninitialized_var(cmt_retries), uninitialized_var(wb_retries);
|
int uninitialized_var(cmt_retries), uninitialized_var(wb_retries);
|
||||||
int err, idx_growth, data_growth, dd_growth;
|
int err, idx_growth, data_growth, dd_growth, retried = 0;
|
||||||
struct retries_info ri;
|
|
||||||
|
|
||||||
ubifs_assert(req->new_page <= 1);
|
ubifs_assert(req->new_page <= 1);
|
||||||
ubifs_assert(req->dirtied_page <= 1);
|
ubifs_assert(req->dirtied_page <= 1);
|
||||||
|
@ -542,7 +485,6 @@ int ubifs_budget_space(struct ubifs_info *c, struct ubifs_budget_req *req)
|
||||||
if (!data_growth && !dd_growth)
|
if (!data_growth && !dd_growth)
|
||||||
return 0;
|
return 0;
|
||||||
idx_growth = calc_idx_growth(c, req);
|
idx_growth = calc_idx_growth(c, req);
|
||||||
memset(&ri, 0, sizeof(struct retries_info));
|
|
||||||
|
|
||||||
again:
|
again:
|
||||||
spin_lock(&c->space_lock);
|
spin_lock(&c->space_lock);
|
||||||
|
@ -580,12 +522,17 @@ again:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = make_free_space(c, &ri);
|
err = make_free_space(c);
|
||||||
|
cond_resched();
|
||||||
if (err == -EAGAIN) {
|
if (err == -EAGAIN) {
|
||||||
dbg_budg("try again");
|
dbg_budg("try again");
|
||||||
cond_resched();
|
|
||||||
goto again;
|
goto again;
|
||||||
} else if (err == -ENOSPC) {
|
} else if (err == -ENOSPC) {
|
||||||
|
if (!retried) {
|
||||||
|
retried = 1;
|
||||||
|
dbg_budg("-ENOSPC, but anyway try once again");
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
dbg_budg("FS is full, -ENOSPC");
|
dbg_budg("FS is full, -ENOSPC");
|
||||||
c->nospace = 1;
|
c->nospace = 1;
|
||||||
if (can_use_rp(c) || c->rp_size == 0)
|
if (can_use_rp(c) || c->rp_size == 0)
|
||||||
|
|
Loading…
Reference in a new issue