[PATCH/RFC] reserved pool
Artem B. Bityutskiy
dedekind at infradead.org
Wed May 10 04:29:02 EDT 2006
On Tue, 2006-05-09 at 16:45 +0400, Artem B. Bityutskiy wrote:
> Hello,
>
> long time I was asked to solve a problem when users eat all the file
> system free space and the system cannot boot because different programs
> can't write any longer (-ENOSPC). I made a straight-forward patch and it
> solved the problem.
The patch is updated
(http://git.infradead.org/?p=users/dedekind/dedekind-mtd-2.6.git;a=commit;h=73229f32650fcefbcc0b0bf8b4c48fae1a70c10d).
diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c
index ea1f37d..f5d4319 100644
--- a/fs/jffs2/fs.c
+++ b/fs/jffs2/fs.c
@@ -348,10 +348,16 @@ void jffs2_dirty_inode(struct inode *ino
int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
{
struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+ int ret;
if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
return -EROFS;
+ /* Re-parse mount options */
+ ret = jffs2_parse_options(sb, data);
+ if (ret)
+ return ret;
+
/* We stop if it was running, then restart if it needs to.
This also catches the case where it was stopped and this
is just a remount to restart it.
diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h
index 4bcfb55..9f6bc18 100644
--- a/fs/jffs2/jffs2_fs_sb.h
+++ b/fs/jffs2/jffs2_fs_sb.h
@@ -97,6 +97,11 @@ struct jffs2_sb_info {
uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */
+ /* Size of the reserved pool. I.e., how many bytes of flash space are
+ * reserved for root. Mortals cannot use it (-ENOSPC is returned). Set
+ * up via mount options. */
+ unsigned int rp_size;
+
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
/* Write-behind buffer for NAND flash */
unsigned char *wbuf;
diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h
index f6645af..ffe4176 100644
--- a/fs/jffs2/nodelist.h
+++ b/fs/jffs2/nodelist.h
@@ -18,6 +18,7 @@ #include <linux/config.h>
#include <linux/fs.h>
#include <linux/types.h>
#include <linux/jffs2.h>
+#include <linux/capability.h>
#include "jffs2_fs_sb.h"
#include "jffs2_fs_i.h"
#include "summary.h"
@@ -255,6 +256,23 @@ #endif
return ret;
}
+/*
+ * Check whether there is enough space for a user to write.
+ */
+static inline int jffs2_user_can_write(struct jffs2_sb_info *c)
+{
+ unsigned int avail;
+
+ if (capable(CAP_SYS_ADMIN))
+ return 1;
+
+ avail = c->dirty_size + c->free_size + c->unchecked_size +
+ c->erasing_size - c->resv_blocks_write * c->sector_size
+ - c->nospc_dirty_size;
+
+ return avail > c->rp_size;
+}
+
#define ALLOC_NORMAL 0 /* Normal allocation */
#define ALLOC_DELETION 1 /* Deletion node. Best to allow it */
#define ALLOC_GC 2 /* Space requested for GC. Give it or die */
@@ -399,6 +417,9 @@ int jffs2_do_mount_fs(struct jffs2_sb_in
/* erase.c */
void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
+
+/* super.c */
+int jffs2_parse_options(struct super_block *sb, char *opts);
#ifdef CONFIG_JFFS2_FS_WRITEBUFFER
/* wbuf.c */
diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c
index 49127a1..547af15 100644
--- a/fs/jffs2/nodemgmt.c
+++ b/fs/jffs2/nodemgmt.c
@@ -57,6 +57,12 @@ int jffs2_reserve_space(struct jffs2_sb_
spin_lock(&c->erase_completion_lock);
+ if (prio != ALLOC_DELETION && !jffs2_user_can_write(c)) {
+ spin_unlock(&c->erase_completion_lock);
+ up(&c->alloc_sem);
+ return -ENOSPC;
+ }
+
/* this needs a little more thought (true <tglx> :)) */
while(ret == -EAGAIN) {
while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index ffd8e84..d9db0bb 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -24,10 +24,13 @@ #include <linux/pagemap.h>
#include <linux/mtd/mtd.h>
#include <linux/ctype.h>
#include <linux/namei.h>
+#include <linux/parser.h>
+#include <linux/seq_file.h>
#include "compr.h"
#include "nodelist.h"
static void jffs2_put_super(struct super_block *);
+static int jffs2_show_options(struct seq_file *m, struct vfsmount
*mnt);
static kmem_cache_t *jffs2_inode_cachep;
@@ -78,6 +81,7 @@ static struct super_operations jffs2_sup
.clear_inode = jffs2_clear_inode,
.dirty_inode = jffs2_dirty_inode,
.sync_fs = jffs2_sync_fs,
+ .show_options = jffs2_show_options,
};
static int jffs2_sb_compare(struct super_block *sb, void *data)
@@ -152,15 +156,15 @@ static struct super_block *jffs2_get_sb_
sb->s_op = &jffs2_super_operations;
sb->s_flags = flags | MS_NOATIME;
- ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+ /* Parse mount options */
+ ret = jffs2_parse_options(sb, data);
+ if (ret)
+ goto error;
+
+ ret = jffs2_do_fill_super(sb, data, flags & MS_SILENT ? 1 : 0);
+ if (ret)
+ goto error;
- if (ret) {
- /* Failure case... */
- up_write(&sb->s_umount);
- deactivate_super(sb);
- return ERR_PTR(ret);
- }
-
sb->s_flags |= MS_ACTIVE;
return sb;
@@ -169,6 +173,12 @@ static struct super_block *jffs2_get_sb_
put_mtd_device(mtd);
return sb;
+
+ error:
+ /* Failure case... */
+ up_write(&sb->s_umount);
+ deactivate_super(sb);
+ return ERR_PTR(ret);
}
static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type
*fs_type,
@@ -271,6 +281,70 @@ static struct super_block *jffs2_get_sb(
out:
path_release(&nd);
return ERR_PTR(err);
+}
+
+/*
+ * Mount option identifiers.
+ */
+enum {
+ JFFS2_OPT_RP_SIZE,
+ JFFS2_OPT_LAST_FAKE
+};
+
+/*
+ * Supported mount options.
+ */
+static struct match_token tokens[] = {
+ {JFFS2_OPT_RP_SIZE, "rpsize=%u"}, /* Size of the reserved pool in KiB
*/
+ {JFFS2_OPT_LAST_FAKE, NULL} /* End of list marker */
+};
+
+#define JFFS2_MAX_ARGS 1
+
+int jffs2_parse_options(struct super_block *sb, char *opts)
+{
+ unsigned int opt;
+ char *p;
+ substring_t args[JFFS2_MAX_ARGS];
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+ if (!opts)
+ return 0;
+
+ while ((p = strsep(&opts, ",")) != NULL) {
+ int token;
+
+ if (!*p)
+ continue;
+
+ token = match_token(p, tokens, args);
+
+ switch (token) {
+ case JFFS2_OPT_RP_SIZE:
+ if (match_int(&args[0], &opt))
+ return -EINVAL;
+ c->rp_size = opt * 1024;
+ if (c->rp_size > c->mtd->size) {
+ JFFS2_WARNING("too large reserved pool size, max is %u KB\n",
+ c->mtd->size/1024);
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int jffs2_show_options(struct seq_file *m, struct vfsmount *mnt)
+{
+ struct jffs2_sb_info *c = JFFS2_SB_INFO(mnt->mnt_sb);
+
+ if (c->rp_size)
+ seq_printf(m, ",rpsize=%d", c->rp_size/1024);
+ return 0;
}
static void jffs2_put_super (struct super_block *sb)
More information about the linux-mtd
mailing list