aboutsummaryrefslogtreecommitdiff
path: root/fs/jffs2/nodemgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jffs2/nodemgmt.c')
-rw-r--r--fs/jffs2/nodemgmt.c62
1 files changed, 56 insertions, 6 deletions
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 6784d1e7a7e..b6bd4affd9a 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -18,6 +18,37 @@
#include "nodelist.h"
#include "debug.h"
+/*
+ * Check whether the user is allowed to write.
+ */
+static int jffs2_rp_can_write(struct jffs2_sb_info *c)
+{
+ uint32_t avail;
+ struct jffs2_mount_opts *opts = &c->mount_opts;
+
+ avail = c->dirty_size + c->free_size + c->unchecked_size +
+ c->erasing_size - c->resv_blocks_write * c->sector_size
+ - c->nospc_dirty_size;
+
+ if (avail < 2 * opts->rp_size)
+ jffs2_dbg(1, "rpsize %u, dirty_size %u, free_size %u, "
+ "erasing_size %u, unchecked_size %u, "
+ "nr_erasing_blocks %u, avail %u, resrv %u\n",
+ opts->rp_size, c->dirty_size, c->free_size,
+ c->erasing_size, c->unchecked_size,
+ c->nr_erasing_blocks, avail, c->nospc_dirty_size);
+
+ if (avail > opts->rp_size)
+ return 1;
+
+ /* Always allow root */
+ if (capable(CAP_SYS_RESOURCE))
+ return 1;
+
+ jffs2_dbg(1, "forbid writing\n");
+ return 0;
+}
+
/**
* jffs2_reserve_space - request physical space to write nodes to flash
* @c: superblock info
@@ -55,6 +86,15 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
spin_lock(&c->erase_completion_lock);
+ /*
+ * Check if the free space is greater then size of the reserved pool.
+ * If not, only allow root to proceed with writing.
+ */
+ if (prio != ALLOC_DELETION && !jffs2_rp_can_write(c)) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
/* this needs a little more thought (true <tglx> :)) */
while(ret == -EAGAIN) {
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
@@ -139,6 +179,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
spin_unlock(&c->erase_completion_lock);
schedule();
+ remove_wait_queue(&c->erase_wait, &wait);
} else
spin_unlock(&c->erase_completion_lock);
} else if (ret)
@@ -158,6 +199,8 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
jffs2_dbg(1, "%s(): ret is %d\n", __func__, ret);
}
}
+
+out:
spin_unlock(&c->erase_completion_lock);
if (!ret)
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
@@ -169,20 +212,25 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize,
uint32_t *len, uint32_t sumsize)
{
- int ret = -EAGAIN;
+ int ret;
minsize = PAD(minsize);
jffs2_dbg(1, "%s(): Requested 0x%x bytes\n", __func__, minsize);
- spin_lock(&c->erase_completion_lock);
- while(ret == -EAGAIN) {
+ while (true) {
+ spin_lock(&c->erase_completion_lock);
ret = jffs2_do_reserve_space(c, minsize, len, sumsize);
if (ret) {
jffs2_dbg(1, "%s(): looping, ret is %d\n",
__func__, ret);
}
+ spin_unlock(&c->erase_completion_lock);
+
+ if (ret == -EAGAIN)
+ cond_resched();
+ else
+ break;
}
- spin_unlock(&c->erase_completion_lock);
if (!ret)
ret = jffs2_prealloc_raw_node_refs(c, c->nextblock, 1);
@@ -375,14 +423,16 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize,
spin_unlock(&c->erase_completion_lock);
ret = jffs2_prealloc_raw_node_refs(c, jeb, 1);
- if (ret)
- return ret;
+
/* Just lock it again and continue. Nothing much can change because
we hold c->alloc_sem anyway. In fact, it's not entirely clear why
we hold c->erase_completion_lock in the majority of this function...
but that's a question for another (more caffeine-rich) day. */
spin_lock(&c->erase_completion_lock);
+ if (ret)
+ return ret;
+
waste = jeb->free_size;
jffs2_link_node_ref(c, jeb,
(jeb->offset + c->sector_size - waste) | REF_OBSOLETE,