diff options
author | KaiGai Kohei <kaigai@ak.jp.nec.com> | 2006-06-29 10:33:02 -0400 |
---|---|---|
committer | David Woodhouse <dwmw2@infradead.org> | 2006-06-29 10:33:02 -0400 |
commit | c6e8c6ccf96e9249805d0e9828b994f4c926ad51 (patch) | |
tree | 0db9c602c20a94d9afbd555a131805de60e75ed2 /fs | |
parent | ea9b6dcc152f09c207117ab121d4fa03d2db282a (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')
-rw-r--r-- | fs/jffs2/xattr.c | 45 |
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 | * -------------------------------------------------- */ |
57 | static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) | 58 | static 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 | ||
397 | static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) | 398 | static 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 | ||
587 | void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) | 589 | void 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 | } |