mtd/fs/jffs2 build.c,1.43,1.44 gc.c,1.91,1.92 nodelist.c,1.73,1.74 nodelist.h,1.89,1.90 os-linux.h,1.22,1.23 readinode.c,1.97,1.98
David Woodhouse
dwmw2 at infradead.org
Fri Jan 17 11:04:19 EST 2003
- Previous message: mtd/fs/jffs2 README.Locking,1.4,1.5 background.c,1.33,1.34 erase.c,1.47,1.48 fs.c,1.21,1.22 gc.c,1.90,1.91 nodelist.c,1.72,1.73 nodemgmt.c,1.88,1.89 wbuf.c,1.22,1.23
- Next message: mtd/include/linux jffs2_fs_sb.h,1.36,1.37
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
Update of /home/cvs/mtd/fs/jffs2
In directory phoenix.infradead.org:/tmp/cvs-serv26967
Modified Files:
build.c gc.c nodelist.c nodelist.h os-linux.h readinode.c
Log Message:
Revamp locking and add wait queue handling for inocache state.
Index: build.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/build.c,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -r1.43 -r1.44
--- build.c 9 Jan 2003 13:59:14 -0000 1.43
+++ build.c 17 Jan 2003 16:04:16 -0000 1.44
@@ -230,6 +230,7 @@
init_MUTEX(&c->alloc_sem);
init_MUTEX(&c->erase_free_sem);
init_waitqueue_head(&c->erase_wait);
+ init_waitqueue_head(&c->inocache_wq);
spin_lock_init(&c->erase_completion_lock);
spin_lock_init(&c->inocache_lock);
Index: gc.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/gc.c,v
retrieving revision 1.91
retrieving revision 1.92
diff -u -r1.91 -r1.92
--- gc.c 14 Jan 2003 14:54:32 -0000 1.91
+++ gc.c 17 Jan 2003 16:04:16 -0000 1.92
@@ -21,6 +21,9 @@
#include <linux/stat.h>
#include "nodelist.h"
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic,
+ struct jffs2_raw_node_ref *raw);
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
@@ -108,6 +111,7 @@
*/
int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
{
+ struct jffs2_inode_cache *ic;
struct jffs2_eraseblock *jeb;
struct jffs2_inode_info *f;
struct jffs2_raw_node_ref *raw;
@@ -135,7 +139,9 @@
D1(jffs2_dump_block_lists(c));
BUG();
}
+ spin_lock(&c->inocache_lock);
ic = jffs2_get_ino_cache(c, c->checked_ino++);
+ spin_unlock(&c->inocache_lock);
if (!ic)
continue;
if (!ic->nlink) {
@@ -219,7 +225,94 @@
spin_unlock(&c->erase_completion_lock);
- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, ref_offset(raw), inum));
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), inum));
+
+ /* Three possibilities:
+ 1. Inode is already in-core. We must iget it and do proper
+ updating to its fragtree, etc.
+ 2. Inode is not in-core, node is REF_PRISTINE. We lock the
+ inocache to prevent a read_inode(), copy the node intact.
+ 3. Inode is not in-core, node is not pristine. We must iget()
+ and take the slow path.
+ */
+ spin_lock(&c->inocache_lock);
+ ic = jffs2_get_ino_cache(c, inum);
+
+ /* This should never fail unless I'm particularly stupid.
+ So we don't check before dereferencing it */
+
+ switch(ic->state) {
+ case INO_STATE_CHECKEDABSENT:
+ /* It's been checked, but it's not currently in-core.
+ We can just copy any pristine nodes, but have
+ to prevent anyone else from doing read_inode() while
+ we're at it, so we set the state accordingly */
+ if (ref_flags(raw) == REF_PRISTINE)
+ ic->state = INO_STATE_GC;
+ D1(printk("Ino #%u is absent but node not REF_PRISTINE. Reading.\n",
+ inum));
+ break;
+
+ case INO_STATE_PRESENT:
+ case INO_STATE_UNCHECKED:
+ /* It's in-core or hasn't been checked. GC must iget() it. */
+ break;
+
+ case INO_STATE_CHECKING:
+ /* Should never happen. We should have finished checking
+ by the time we actually start doing any GC. */
+ BUG();
+
+
+ case INO_STATE_GC:
+ /* Should never happen. We are holding the alloc_sem,
+ no other garbage collection can happen. Note that we
+ do depend on this later when deciding to do a simple
+ node copy */
+ BUG();
+
+ case INO_STATE_READING:
+ /* Someone's currently trying to read it. We must wait for
+ them to finish and then go through the full iget() route
+ to do the GC. However, sometimes read_inode() needs to get
+ the alloc_sem() (for marking nodes invalid) so we must
+ drop the alloc_sem before sleeping. */
+
+ up(&c->alloc_sem);
+ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
+ inum, ic->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ /* And because we dropped the alloc_sem we must start again from the
+ beginning. Ponder chance of livelock here -- we're returning success
+ without actually making any progress.
+
+ Q: What are the chances that the inode is back in INO_STATE_READING
+ again by the time we next enter this function? And that this happens
+ enough times to cause a real delay?
+
+ A: Small enough that I don't care :)
+ */
+ return 0;
+
+ }
+
+ spin_unlock(&c->inocache_lock);
+
+ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
+ node intact, and we don't have to muck about with the fragtree etc.
+ because we know it's not in-core. If it _was_ in-core, we go through
+ all the iget() crap anyway */
+
+ if (ic->state == INO_STATE_GC) {
+ ret = jffs2_garbage_collect_pristine(c, ic, raw);
+ if (ret != -EBADFD) {
+ up(&c->alloc_sem);
+ goto eraseit_lock;
+ }
+ /* Fall through if it wanted us to */
+ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+ }
+
inode = iget(OFNI_BS_2SFFJ(c), inum);
if (is_bad_inode(inode)) {
@@ -325,6 +418,13 @@
spin_unlock(&c->erase_completion_lock);
return ret;
+}
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c,
+ struct jffs2_inode_cache *ic,
+ struct jffs2_raw_node_ref *raw)
+{
+ D1(printk(KERN_DEBUG "Wanted to GC REF_PRISTINE node. Not implemented yet :)\n"));
+ return -EBADFD;
}
static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
Index: nodelist.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.c,v
retrieving revision 1.73
retrieving revision 1.74
diff -u -r1.73 -r1.74
--- nodelist.c 14 Jan 2003 14:54:32 -0000 1.73
+++ nodelist.c 17 Jan 2003 16:04:16 -0000 1.74
@@ -417,12 +417,24 @@
return err;
}
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
+{
+ spin_lock(&c->inocache_lock);
+ ic->state = state;
+ wake_up(&c->inocache_wq);
+ spin_unlock(&c->inocache_lock);
+}
+
+/* During mount, this needs no locking. During normal operation, its
+ callers want to do other stuff while still holding the inocache_lock.
+ Rather than introducing special case get_ino_cache functions or
+ callbacks, we just let the caller do the locking itself. */
+
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino)
{
struct jffs2_inode_cache *ret;
D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
- spin_lock (&c->inocache_lock);
ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
while (ret && ret->ino < ino) {
@@ -431,8 +443,6 @@
if (ret && ret->ino != ino)
ret = NULL;
-
- spin_unlock(&c->inocache_lock);
D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino));
return ret;
Index: nodelist.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/nodelist.h,v
retrieving revision 1.89
retrieving revision 1.90
diff -u -r1.89 -r1.90
--- nodelist.h 12 Jan 2003 13:21:28 -0000 1.89
+++ nodelist.h 17 Jan 2003 16:04:16 -0000 1.90
@@ -103,13 +103,18 @@
uint32_t ino;
int nlink;
int state;
-#define INO_STATE_UNCHECKED 0
-#define INO_STATE_CHECKING 1
-#define INO_STATE_CHECKEDABSENT 2
-#define INO_STATE_READINGINODE 3
-#define INO_STATE_PRESENT 5
};
+/* Inode states for 'state' above. We need the 'GC' state to prevent
+ someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
+ node without going through all the iget() nonsense */
+#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */
+#define INO_STATE_CHECKING 1 /* CRC checks in progress */
+#define INO_STATE_PRESENT 2 /* In core */
+#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */
+#define INO_STATE_GC 4 /* GCing a 'pristine' node */
+#define INO_STATE_READING 5 /* In read_inode() */
+
#define INOCACHE_HASHSIZE 128
struct jffs2_scan_info {
@@ -286,6 +291,7 @@
struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
uint32_t *highest_version, uint32_t *latest_mctime,
uint32_t *mctime_ver);
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino);
void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
Index: os-linux.h
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/os-linux.h,v
retrieving revision 1.22
retrieving revision 1.23
diff -u -r1.22 -r1.23
--- os-linux.h 14 Jan 2003 14:06:22 -0000 1.22
+++ os-linux.h 17 Jan 2003 16:04:16 -0000 1.23
@@ -69,6 +69,16 @@
#define current_sig_lock current->sig->siglock
#endif
+#define sleep_on_spinunlock(wq, s) \
+ do { \
+ DECLARE_WAITQUEUE(__wait, current); \
+ add_wait_queue((wq), &__wait); \
+ set_current_state(TASK_UNINTERRUPTIBLE); \
+ spin_unlock(s); \
+ schedule(); \
+ remove_wait_queue((wq), &__wait); \
+ } while(0)
+
static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
{
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
Index: readinode.c
===================================================================
RCS file: /home/cvs/mtd/fs/jffs2/readinode.c,v
retrieving revision 1.97
retrieving revision 1.98
diff -u -r1.97 -r1.98
--- readinode.c 14 Jan 2003 14:06:22 -0000 1.97
+++ readinode.c 17 Jan 2003 16:04:16 -0000 1.98
@@ -329,10 +329,43 @@
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
+retry_inocache:
+ spin_lock(&c->inocache_lock);
f->inocache = jffs2_get_ino_cache(c, ino);
D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+ if (f->inocache) {
+ /* Check its state. We may need to wait before we can use it */
+ switch(f->inocache->state) {
+ case INO_STATE_UNCHECKED:
+ case INO_STATE_CHECKEDABSENT:
+ f->inocache->state = INO_STATE_READING;
+ break;
+
+ case INO_STATE_CHECKING:
+ case INO_STATE_GC:
+ /* If it's in either of these states, we need
+ to wait for whoever's got it to finish and
+ put it back. */
+ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+ ino, f->inocache->state));
+ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+ goto retry_inocache;
+
+ case INO_STATE_READING:
+ case INO_STATE_PRESENT:
+ /* Eep. This should never happen. It can
+ happen if Linux calls read_inode() again
+ before clear_inode() has finished though. */
+ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+ /* Fail. That's probably better than allowing it to succeed */
+ f->inocache = NULL;
+ break;
+ }
+ }
+ spin_unlock(&c->inocache_lock);
+
if (!f->inocache && ino == 1) {
/* Special case - no root inode on medium */
f->inocache = jffs2_alloc_inode_cache();
@@ -344,6 +377,7 @@
memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
f->inocache->ino = f->inocache->nlink = 1;
f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+ f->inocache->state = INO_STATE_READING;
jffs2_add_ino_cache(c, f->inocache);
}
if (!f->inocache) {
@@ -357,6 +391,7 @@
if (ret) {
printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", ino, ret);
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return ret;
}
f->dents = fd_list;
@@ -391,6 +426,7 @@
if (ino != 1) {
printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", ino);
if (!fd_list) {
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
return -EIO;
}
printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
@@ -401,6 +437,7 @@
latest_node->isize = cpu_to_je32(0);
latest_node->gid = cpu_to_je16(0);
latest_node->uid = cpu_to_je16(0);
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
return 0;
}
@@ -476,7 +513,7 @@
f->fragtree = RB_ROOT;
break;
}
- f->inocache->state = INO_STATE_PRESENT;
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
return 0;
}
@@ -520,7 +557,7 @@
}
if (f->inocache)
- f->inocache->state = INO_STATE_CHECKEDABSENT;
+ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
up(&f->sem);
- Previous message: mtd/fs/jffs2 README.Locking,1.4,1.5 background.c,1.33,1.34 erase.c,1.47,1.48 fs.c,1.21,1.22 gc.c,1.90,1.91 nodelist.c,1.72,1.73 nodemgmt.c,1.88,1.89 wbuf.c,1.22,1.23
- Next message: mtd/include/linux jffs2_fs_sb.h,1.36,1.37
- Messages sorted by:
[ date ]
[ thread ]
[ subject ]
[ author ]
More information about the linux-mtd-cvs
mailing list