/** * make_free_space - make more free space on the file-system. * @c: UBIFS file-system description object * * 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: * o budgeting is pessimistic, so it always budgets more than it is actually * needed, so shrinking the liability is one way to make free space - the * cached data will take less space then it was budgeted for; * o GC may turn some dark space into free space (budgeting treats dark space * as not available); * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs. * * So this function tries to do the above. Returns %-EAGAIN if some free space * was presumably made and the caller has to re-try budgeting the operation. * Returns %-ENOSPC if it couldn't do more free space, and other negative error * codes on failures. */ static int make_free_space(struct ubifs_info *c) { int err, retries = 0; long long liab1, liab2; do { liab1 = get_liability(c); /* * We probably have some dirty pages or inodes (liability), try * to write them back. */ dbg_budg("liability %lld, run write-back", liab1); shrink_liability(c, NR_TO_WRITE); liab2 = get_liability(c); if (liab2 < liab1) return -EAGAIN; dbg_budg("new liability %lld (not shrunk)", liab2); /* Liability did not shrink again, try GC */ dbg_budg("Run GC"); err = run_gc(c); if (!err) return -EAGAIN; if (err != -EAGAIN && err != -ENOSPC) /* Some real error happened */ return err; dbg_budg("Run commit (retries %d)", retries); err = ubifs_run_commit(c); if (err) return err; } while (retries++ < MAX_MKSPC_RETRIES); return -ENOSPC; }
/** * make_reservation - reserve journal space. * @c: UBIFS file-system description object * @jhead: journal head * @len: how many bytes to reserve * * This function makes space reservation in journal head @jhead. The function * takes the commit lock and locks the journal head, and the caller has to * unlock the head and finish the reservation with 'finish_reservation()'. * Returns zero in case of success and a negative error code in case of * failure. * * Note, the journal head may be unlocked as soon as the data is written, while * the commit lock has to be released after the data has been added to the * TNC. */ static int make_reservation(struct ubifs_info *c, int jhead, int len) { int err, cmt_retries = 0, nospc_retries = 0; ubifs_assert(len <= c->dark_wm); again: down_read(&c->commit_sem); err = reserve_space(c, jhead, len); if (!err) return 0; up_read(&c->commit_sem); if (err == -ENOSPC) { /* * GC could not make any progress. We should try to commit * once because it could make some dirty space and GC would * make progress, so make the error -EAGAIN so that the below * will commit and re-try. */ if (nospc_retries++ < 2) { dbg_jrn("no space, retry"); err = -EAGAIN; } /* * This means that the budgeting is incorrect. We always have * to be able to write to the media, because all operations are * budgeted. Deletions are not budgeted, though, but we reserve * an extra LEB for them. */ } if (err != -EAGAIN) goto out; /* * -EAGAIN means that the journal is full or too large, or the above * code wants to do one commit. Do this and re-try. */ if (cmt_retries > 128) { /* * This should not happen unless the journal size limitations * are too tough. */ ubifs_err("stuck in space allocation"); err = -ENOSPC; goto out; } else if (cmt_retries > 32) ubifs_warn("too many space allocation re-tries (%d)", cmt_retries); dbg_jrn("-EAGAIN, commit and retry (retried %d times)", cmt_retries); cmt_retries += 1; err = ubifs_run_commit(c); if (err) return err; goto again; out: ubifs_err("cannot reserve %d bytes in jhead %d, error %d", len, jhead, err); if (err == -ENOSPC) { /* This are some budgeting problems, print useful information */ down_write(&c->commit_sem); spin_lock(&c->space_lock); dbg_dump_stack(); dbg_dump_budg(c); spin_unlock(&c->space_lock); dbg_dump_lprops(c); cmt_retries = dbg_check_lprops(c); up_write(&c->commit_sem); } return err; }
/** * make_free_space - make more free space on the file-system. * @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 * is supposedly no free space. But in most cases there is some free space: * o budgeting is pessimistic, so it always budgets more then it is actually * needed, so shrinking the liability is one way to make free space - the * cached data will take less space then it was budgeted for; * o GC may turn some dark space into free space (budgeting treats dark space * as not available); * o commit may free some LEB, i.e., turn freeable LEBs into free LEBs. * * So this function tries to do the above. Returns %-EAGAIN if some free space * was presumably made and the caller has to re-try budgeting the operation. * Returns %-ENOSPC if it couldn't do more free space, and other negative error * codes on failures. */ static int make_free_space(struct ubifs_info *c, struct retries_info *ri) { int err; /* * If we have some dirty pages and inodes (liability), try to write * them back unless this was tried too many times without effect * already. */ if (ri->shrink_retries < MAX_SHRINK_RETRIES && !ri->try_gc) { long long liability; spin_lock(&c->space_lock); liability = c->budg_idx_growth + c->budg_data_growth + c->budg_dd_growth; spin_unlock(&c->space_lock); if (ri->prev_liability >= liability) { /* 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); shrink_liability(c, NR_TO_WRITE + ri->shrink_cnt); 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); if (!err) return -EAGAIN; if (err == -EAGAIN) { dbg_budg("GC asked to commit"); err = ubifs_run_commit(c); if (err) return err; return -EAGAIN; } if (err != -ENOSPC) return err; /* * 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); if (err) return err; return -EAGAIN; } return -ENOSPC; }