diff --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 091ab0eaabbeaca4fae892abfcf0accc38442492..19d768ef221780144ae50c5d024a8d0e54a0ac84 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -159,6 +159,26 @@ static int jffs2_readdir(struct file *file, struct dir_context *ctx) /***********************************************************************/ +static void jffs2_iget_failed(struct jffs2_sb_info *c, struct inode *inode) +{ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + + /* + * Reset pino_nlink to zero, so jffs2_do_clear_inode() will mark + * all flash nodes used by the inode as obsolete and GC procedure + * will reclaim these flash nodes, else these flash spaces will be + * unreclaimable forever. + * + * Update pino_nlink under inocache_lock, because no proceses could + * get the inode due to I_NEW flag, and only GC procedure may try to + * read pino_nlink under inocache_lock. + */ + spin_lock(&c->inocache_lock); + f->inocache->pino_nlink = 0; + spin_unlock(&c->inocache_lock); + + iget_failed(inode); +} static int jffs2_create(struct mnt_idmap *idmap, struct inode *dir_i, struct dentry *dentry, umode_t mode, bool excl) @@ -217,7 +237,7 @@ static int jffs2_create(struct mnt_idmap *idmap, struct inode *dir_i, return 0; fail: - iget_failed(inode); + jffs2_iget_failed(c, inode); jffs2_free_raw_inode(ri); return ret; } @@ -439,7 +459,7 @@ static int jffs2_symlink (struct mnt_idmap *idmap, struct inode *dir_i, return 0; fail: - iget_failed(inode); + jffs2_iget_failed(c, inode); return ret; } @@ -585,7 +605,7 @@ static int jffs2_mkdir (struct mnt_idmap *idmap, struct inode *dir_i, return 0; fail: - iget_failed(inode); + jffs2_iget_failed(c, inode); return ret; } @@ -762,7 +782,7 @@ static int jffs2_mknod (struct mnt_idmap *idmap, struct inode *dir_i, return 0; fail: - iget_failed(inode); + jffs2_iget_failed(c, inode); return ret; } diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index b86c78d178c60a3af10f6ae4c688d6acce8f8dbf..c3b0d56e7007e9a936a3e9e8a914358502eefadf 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -469,6 +469,13 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) while ((*prev) && (*prev)->ino < old->ino) { prev = &(*prev)->next; } + + /* + * It's possible that we can not find the inocache in + * hash table because it had been removed by + * jffs2_remove_node_refs_from_ino_list(), but it's still not freed, + * so we need go forward and free it. + */ if ((*prev) == old) { *prev = old->next; } diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 03b4f99614bef86ee840e34131b652675d7fa8b6..00145ae413566999a1595f1947c88984d2380d31 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -1344,6 +1344,7 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, case INO_STATE_CHECKING: case INO_STATE_GC: + case INO_STATE_CLEARING: /* If it's in either of these states, we need to wait for whoever's got it to finish and put it back. */ @@ -1438,8 +1439,16 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) } if (f->inocache && f->inocache->state != INO_STATE_CHECKING) { - jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + bool need_del = false; + + spin_lock(&c->erase_completion_lock); if (f->inocache->nodes == (void *)f->inocache) + need_del = true; + jffs2_set_inocache_state(c, f->inocache, + INO_STATE_CHECKEDABSENT); + spin_unlock(&c->erase_completion_lock); + + if (need_del) jffs2_del_ino_cache(c, f->inocache); } diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 3b6bdc9a49e1b0d25f87263c3a29705cd4b4258c..1e65b7b06c226a6b5f8d41a2df31b837abc2c509 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -573,6 +573,15 @@ static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct return ref; /* success */ } +static void move_xattr_ref_to_dead_list(struct jffs2_sb_info *c, + struct jffs2_xattr_ref *ref) +{ + spin_lock(&c->erase_completion_lock); + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + spin_unlock(&c->erase_completion_lock); +} + static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { /* must be called under down_write(xattr_sem) */ @@ -582,10 +591,7 @@ static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re ref->xseqno |= XREF_DELETE_MARKER; ref->ino = ref->ic->ino; ref->xid = ref->xd->xid; - spin_lock(&c->erase_completion_lock); - ref->next = c->xref_dead_list; - c->xref_dead_list = ref; - spin_unlock(&c->erase_completion_lock); + move_xattr_ref_to_dead_list(c, ref); dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", ref->ino, ref->xid, ref->xseqno); @@ -1094,6 +1100,40 @@ int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, return rc; } +static void do_jffs2_delete_xattr_ref(struct jffs2_sb_info *c, + struct jffs2_xattr_ref *ref) +{ + uint32_t request, length; + int err; + struct jffs2_xattr_datum *xd; + + request = PAD(sizeof(struct jffs2_raw_xref)); + err = jffs2_reserve_space(c, request, &length, + ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE); + down_write(&c->xattr_sem); + if (err) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", + err, request); + delete_xattr_ref(c, ref); + up_write(&c->xattr_sem); + return; + } + + xd = ref->xd; + ref->ino = ref->ic->ino; + ref->xid = xd->xid; + ref->xseqno |= XREF_DELETE_MARKER; + save_xattr_ref(c, ref); + + move_xattr_ref_to_dead_list(c, ref); + dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", + ref->ino, ref->xid, ref->xseqno); + unrefer_xattr_datum(c, xd); + + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); +} + int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, const char *buffer, size_t size, int flags) { @@ -1101,7 +1141,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); struct jffs2_inode_cache *ic = f->inocache; struct jffs2_xattr_datum *xd; - struct jffs2_xattr_ref *ref, *newref, **pref; + struct jffs2_xattr_ref *ref, *newref, *oldref, **pref; uint32_t length, request; int rc; @@ -1117,6 +1157,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, return rc; } + oldref = NULL; /* Find existing xattr */ down_write(&c->xattr_sem); retry: @@ -1200,11 +1241,13 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, rc = PTR_ERR(newref); unrefer_xattr_datum(c, xd); } else if (ref) { - delete_xattr_ref(c, ref); + oldref = ref; } out: up_write(&c->xattr_sem); jffs2_complete_reservation(c); + if (oldref) + do_jffs2_delete_xattr_ref(c, oldref); return rc; }