aboutsummaryrefslogtreecommitdiffstats
path: root/fs/jffs2
diff options
context:
space:
mode:
authorKaiGai Kohei <kaigai@ak.jp.nec.com>2006-06-29 10:33:02 -0400
committerDavid Woodhouse <dwmw2@infradead.org>2006-06-29 10:33:02 -0400
commitc6e8c6ccf96e9249805d0e9828b994f4c926ad51 (patch)
tree0db9c602c20a94d9afbd555a131805de60e75ed2 /fs/jffs2
parentea9b6dcc152f09c207117ab121d4fa03d2db282a (diff)
[JFFS2][XATTR] Fix xd->refcnt race condition
When xd->refcnt is checked whether this xdatum should be released or not, atomic_dec_and_lock() is used to ensure holding the c->erase_completion_lock. This fix change a specification of delete_xattr_datum(). Previously, it's only called when xd->refcnt equals zero. (calling it with positive xd->refcnt cause a BUG()) If you applied this patch, the function checks whether xd->refcnt is zero or not under the spinlock if necessary. Then, it marks xd DEAD flahs and links with xattr_dead_list or releases it immediately when xd->refcnt become zero. Signed-off-by: KaiGai Kohei <kaigai@ak.jp.nec.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org>
Diffstat (limited to 'fs/jffs2')
-rw-r--r--fs/jffs2/xattr.c45
1 files changed, 22 insertions, 23 deletions
diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c
index 18e66dbf23b4..25bc1ae08648 100644
--- a/fs/jffs2/xattr.c
+++ b/fs/jffs2/xattr.c
@@ -50,9 +50,10 @@
50 * is used to write xdatum to medium. xd->version will be incremented. 50 * is used to write xdatum to medium. xd->version will be incremented.
51 * create_xattr_datum(c, xprefix, xname, xvalue, xsize) 51 * create_xattr_datum(c, xprefix, xname, xvalue, xsize)
52 * is used to create new xdatum and write to medium. 52 * is used to create new xdatum and write to medium.
53 * delete_xattr_datum(c, xd) 53 * unrefer_xattr_datum(c, xd)
54 * is used to delete a xdatum. It marks xd JFFS2_XFLAGS_DEAD, and allows 54 * is used to delete a xdatum. When nobody refers this xdatum, JFFS2_XFLAGS_DEAD
55 * GC to reclaim those physical nodes. 55 * is set on xd->flags and chained xattr_dead_list or release it immediately.
56 * In the first case, the garbage collector release it later.
56 * -------------------------------------------------- */ 57 * -------------------------------------------------- */
57static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) 58static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize)
58{ 59{
@@ -394,22 +395,24 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c,
394 return xd; 395 return xd;
395} 396}
396 397
397static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) 398static void unrefer_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd)
398{ 399{
399 /* must be called under down_write(xattr_sem) */ 400 /* must be called under down_write(xattr_sem) */
400 BUG_ON(atomic_read(&xd->refcnt)); 401 if (atomic_dec_and_lock(&xd->refcnt, &c->erase_completion_lock)) {
402 uint32_t xid = xd->xid, version = xd->version;
401 403
402 unload_xattr_datum(c, xd); 404 unload_xattr_datum(c, xd);
403 xd->flags |= JFFS2_XFLAGS_DEAD; 405 xd->flags |= JFFS2_XFLAGS_DEAD;
404 spin_lock(&c->erase_completion_lock); 406 if (xd->node == (void *)xd) {
405 if (xd->node == (void *)xd) { 407 BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID));
406 BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID)); 408 jffs2_free_xattr_datum(xd);
407 jffs2_free_xattr_datum(xd); 409 } else {
408 } else { 410 list_add(&xd->xindex, &c->xattr_dead_list);
409 list_add(&xd->xindex, &c->xattr_dead_list); 411 }
412 spin_unlock(&c->erase_completion_lock);
413
414 dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xid, version);
410 } 415 }
411 spin_unlock(&c->erase_completion_lock);
412 dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xd->xid, xd->version);
413} 416}
414 417
415/* -------- xref related functions ------------------ 418/* -------- xref related functions ------------------
@@ -580,8 +583,7 @@ static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re
580 dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", 583 dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n",
581 ref->ino, ref->xid, ref->xseqno); 584 ref->ino, ref->xid, ref->xseqno);
582 585
583 if (atomic_dec_and_test(&xd->refcnt)) 586 unrefer_xattr_datum(c, xd);
584 delete_xattr_datum(c, xd);
585} 587}
586 588
587void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) 589void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
@@ -1119,8 +1121,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
1119 ref->next = c->xref_dead_list; 1121 ref->next = c->xref_dead_list;
1120 c->xref_dead_list = ref; 1122 c->xref_dead_list = ref;
1121 spin_unlock(&c->erase_completion_lock); 1123 spin_unlock(&c->erase_completion_lock);
1122 if (atomic_dec_and_test(&xd->refcnt)) 1124 unrefer_xattr_datum(c, xd);
1123 delete_xattr_datum(c, xd);
1124 } else { 1125 } else {
1125 ref->ic = ic; 1126 ref->ic = ic;
1126 ref->xd = xd; 1127 ref->xd = xd;
@@ -1156,8 +1157,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
1156 down_write(&c->xattr_sem); 1157 down_write(&c->xattr_sem);
1157 if (rc) { 1158 if (rc) {
1158 JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); 1159 JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request);
1159 if (atomic_dec_and_test(&xd->refcnt)) 1160 unrefer_xattr_datum(c, xd);
1160 delete_xattr_datum(c, xd);
1161 up_write(&c->xattr_sem); 1161 up_write(&c->xattr_sem);
1162 return rc; 1162 return rc;
1163 } 1163 }
@@ -1170,8 +1170,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname,
1170 ic->xref = ref; 1170 ic->xref = ref;
1171 } 1171 }
1172 rc = PTR_ERR(newref); 1172 rc = PTR_ERR(newref);
1173 if (atomic_dec_and_test(&xd->refcnt)) 1173 unrefer_xattr_datum(c, xd);
1174 delete_xattr_datum(c, xd);
1175 } else if (ref) { 1174 } else if (ref) {
1176 delete_xattr_ref(c, ref); 1175 delete_xattr_ref(c, ref);
1177 } 1176 }