@@ -403,6 +403,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c)
INIT_LIST_HEAD(&c->free_list);
INIT_LIST_HEAD(&c->bad_list);
INIT_LIST_HEAD(&c->bad_used_list);
+ INIT_LIST_HEAD(&c->prevent_gc_list);
c->highest_ino = 1;
c->summary = NULL;
@@ -164,6 +164,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
struct jffs2_inode_info *f, *dir_f;
struct jffs2_sb_info *c;
struct inode *inode;
+ struct prevent_block pb;
int ret;
ri = jffs2_alloc_raw_inode();
@@ -197,7 +198,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
no chance of AB-BA deadlock involving its f->sem). */
mutex_unlock(&f->sem);
- ret = jffs2_do_create(c, dir_f, f, ri, &dentry->d_name);
+ ret = jffs2_do_create(c, dir_f, f, ri, &dentry->d_name, &pb);
if (ret)
goto fail;
@@ -210,10 +211,12 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry,
f->inocache->pino_nlink, inode->i_mapping->nrpages);
d_instantiate_new(dentry, inode);
+ remove_prevent_gc_list(c, &pb);
return 0;
fail:
iget_failed(inode);
+ remove_prevent_gc_list(c, &pb);
jffs2_free_raw_inode(ri);
return ret;
}
@@ -285,6 +288,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
+ struct prevent_block pb;
int namelen;
uint32_t alloclen;
int ret, targetlen = strlen(target);
@@ -346,6 +350,8 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
goto fail;
}
+ add_prevent_gc_list(c, &pb);
+
/* We use f->target field to store the target path. */
f->target = kmemdup(target, targetlen + 1, GFP_KERNEL);
if (!f->target) {
@@ -430,10 +436,12 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char
jffs2_complete_reservation(c);
d_instantiate_new(dentry, inode);
+ remove_prevent_gc_list(c, &pb);
return 0;
fail:
iget_failed(inode);
+ remove_prevent_gc_list(c, &pb);
return ret;
}
@@ -447,6 +455,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
+ struct prevent_block pb;
int namelen;
uint32_t alloclen;
int ret;
@@ -503,6 +512,9 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode
ret = PTR_ERR(fn);
goto fail;
}
+
+ add_prevent_gc_list(c, &pb);
+
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
@@ -574,10 +586,12 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, umode_t mode
jffs2_complete_reservation(c);
d_instantiate_new(dentry, inode);
+ remove_prevent_gc_list(c, &pb);
return 0;
fail:
iget_failed(inode);
+ remove_prevent_gc_list(c, &pb);
return ret;
}
@@ -614,6 +628,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
struct jffs2_full_dirent *fd;
+ struct prevent_block pb;
int namelen;
union jffs2_device_node dev;
int devlen = 0;
@@ -672,6 +687,9 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
ret = PTR_ERR(fn);
goto fail;
}
+
+ add_prevent_gc_list(c, &pb);
+
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/
@@ -745,10 +763,12 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, umode_t mode
jffs2_complete_reservation(c);
d_instantiate_new(dentry, inode);
+ remove_prevent_gc_list(c, &pb);
return 0;
fail:
iget_failed(inode);
+ remove_prevent_gc_list(c, &pb);
return ret;
}
@@ -95,6 +95,10 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
}
ret = list_entry(nextlist->next, struct jffs2_eraseblock, list);
+ if (is_prevented_block(c, ret)) {
+ n = jiffies % 128;
+ goto again;
+ }
list_del(&ret->list);
c->gcblock = ret;
ret->gc_node = ret->first_node;
@@ -106,6 +106,7 @@ struct jffs2_sb_info {
struct list_head free_list; /* Blocks which are free and ready to be used */
struct list_head bad_list; /* Bad blocks. */
struct list_head bad_used_list; /* Bad blocks with valid data in. */
+ struct list_head prevent_gc_list; /* Prevent blocks picked for gc. */
spinlock_t erase_completion_lock; /* Protect free_list and erasing_list
against erase completion handler */
@@ -98,6 +98,12 @@ struct jffs2_raw_node_ref
/* Use blocks of about 256 bytes */
#define REFS_PER_BLOCK ((255/sizeof(struct jffs2_raw_node_ref))-1)
+struct prevent_block {
+ struct list_head list;
+ struct jffs2_eraseblock *jeb;
+ int in_use;
+};
+
static inline struct jffs2_raw_node_ref *ref_next(struct jffs2_raw_node_ref *ref)
{
ref++;
@@ -405,7 +411,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
struct jffs2_raw_inode *ri, unsigned char *buf,
uint32_t offset, uint32_t writelen, uint32_t *retlen);
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f,
- struct jffs2_raw_inode *ri, const struct qstr *qstr);
+ struct jffs2_raw_inode *ri, const struct qstr *qstr, struct prevent_block *pb);
int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name,
int namelen, struct jffs2_inode_info *dead_f, uint32_t time);
int jffs2_do_link(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino,
@@ -479,6 +485,44 @@ int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc
int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
#endif
+static inline void add_prevent_gc_list(struct jffs2_sb_info *c, struct prevent_block *pb)
+{
+ BUG_ON(!mutex_is_locked(&c->alloc_sem));
+ pb->jeb = c->nextblock;
+ pb->in_use = 1;
+ list_add(&pb->list, &c->prevent_gc_list);
+}
+
+static inline void remove_prevent_gc_list(struct jffs2_sb_info *c, struct prevent_block *pb)
+{
+ if (pb->in_use == 1) {
+ mutex_lock(&c->alloc_sem);
+ list_del(&pb->list);
+ mutex_unlock(&c->alloc_sem);
+ }
+ pb->in_use = 0;
+}
+
+static inline int is_prevented_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+ int ret = 0;
+ struct list_head *this = NULL;
+ struct prevent_block *pb = NULL;
+
+ BUG_ON(!mutex_is_locked(&c->alloc_sem));
+ if (list_empty(&c->prevent_gc_list))
+ goto out;
+
+ list_for_each(this, &c->prevent_gc_list) {
+ pb = list_entry(this, struct prevent_block, list);
+ if (pb->jeb != NULL && pb->jeb->offset == jeb->offset) {
+ ret = 1;
+ break;
+ }
+ }
+out:
+ return ret;
+}
#include "debug.h"
#endif /* __JFFS2_NODELIST_H__ */
@@ -440,7 +440,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
struct jffs2_inode_info *f, struct jffs2_raw_inode *ri,
- const struct qstr *qstr)
+ const struct qstr *qstr, struct prevent_block *pb)
{
struct jffs2_raw_dirent *rd;
struct jffs2_full_dnode *fn;
@@ -474,6 +474,9 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
jffs2_complete_reservation(c);
return PTR_ERR(fn);
}
+
+ add_prevent_gc_list(c, pb);
+
/* No data here. Only a metadata node, which will be
obsoleted by the first data write
*/