From c9f700f840bd481b3e01fcad1ba8da01794a6726 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sun, 11 Jun 2006 10:35:15 +0900 Subject: [JFFS2][XATTR] using 'delete marker' for xdatum/xref deletion - When xdatum is removed, a new xdatum with 'delete marker' is written. (version==0xffffffff means 'delete marker') - When xref is removed, a new xref with 'delete marker' is written. (odd-numbered xseqno means 'delete marker') - delete_xattr_(datum/xref)_delay() are new deletion functions are added. We can only use them if we can detect the target obsolete xdatum/xref as a orphan or errir one. (e.g when inode deletion, or detecting crc error) [1/3] jffs2-xattr-v6-01-delete_marker.patch Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/erase.c | 19 +- fs/jffs2/gc.c | 5 +- fs/jffs2/jffs2_fs_sb.h | 3 + fs/jffs2/malloc.c | 2 + fs/jffs2/nodemgmt.c | 21 +- fs/jffs2/scan.c | 57 ++-- fs/jffs2/summary.c | 39 +-- fs/jffs2/xattr.c | 736 +++++++++++++++++++++++++++++++------------------ fs/jffs2/xattr.h | 40 ++- 9 files changed, 583 insertions(+), 339 deletions(-) (limited to 'fs/jffs2') diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index 1862e8bc101..1644e340885 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -230,7 +230,6 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, at the end of the linked list. Stash it and continue from the beginning of the list */ ic = (struct jffs2_inode_cache *)(*prev); - BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE); prev = &ic->nodes; continue; } @@ -254,7 +253,8 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, /* PARANOIA */ if (!ic) { - printk(KERN_WARNING "inode_cache not found in remove_node_refs()!!\n"); + JFFS2_WARNING("inode_cache/xattr_datum/xattr_ref" + " not found in remove_node_refs()!!\n"); return; } @@ -279,8 +279,19 @@ static inline void jffs2_remove_node_refs_from_ino_list(struct jffs2_sb_info *c, printk("\n"); }); - if (ic->nodes == (void *)ic && ic->nlink == 0) - jffs2_del_ino_cache(c, ic); + switch (ic->class) { +#ifdef CONFIG_JFFS2_FS_XATTR + case RAWNODE_CLASS_XATTR_DATUM: + jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic); + break; + case RAWNODE_CLASS_XATTR_REF: + jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic); + break; +#endif + default: + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + } } void jffs2_free_jeb_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 477c526d638..f59b147661c 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -275,13 +275,12 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) * We can decide whether this node is inode or xattr by ic->class. */ if (ic->class == RAWNODE_CLASS_XATTR_DATUM || ic->class == RAWNODE_CLASS_XATTR_REF) { - BUG_ON(raw->next_in_ino != (void *)ic); spin_unlock(&c->erase_completion_lock); if (ic->class == RAWNODE_CLASS_XATTR_DATUM) { - ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic); + ret = jffs2_garbage_collect_xattr_datum(c, (struct jffs2_xattr_datum *)ic, raw); } else { - ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic); + ret = jffs2_garbage_collect_xattr_ref(c, (struct jffs2_xattr_ref *)ic, raw); } goto release_sem; } diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index 935fec1b120..b98594992ee 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -119,8 +119,11 @@ struct jffs2_sb_info { #ifdef CONFIG_JFFS2_FS_XATTR #define XATTRINDEX_HASHSIZE (57) uint32_t highest_xid; + uint32_t highest_xseqno; struct list_head xattrindex[XATTRINDEX_HASHSIZE]; struct list_head xattr_unchecked; + struct list_head xattr_dead_list; + struct jffs2_xattr_ref *xref_dead_list; struct jffs2_xattr_ref *xref_temp; struct rw_semaphore xattr_sem; uint32_t xdatum_mem_usage; diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index 4889d0700c0..8310c95478e 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -291,6 +291,7 @@ struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void) memset(xd, 0, sizeof(struct jffs2_xattr_datum)); xd->class = RAWNODE_CLASS_XATTR_DATUM; + xd->node = (void *)xd; INIT_LIST_HEAD(&xd->xindex); return xd; } @@ -309,6 +310,7 @@ struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void) memset(ref, 0, sizeof(struct jffs2_xattr_ref)); ref->class = RAWNODE_CLASS_XATTR_REF; + ref->node = (void *)ref; return ref; } diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 8bedfd2ff68..01594a2256e 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -684,19 +684,26 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref spin_lock(&c->erase_completion_lock); ic = jffs2_raw_ref_to_ic(ref); - /* It seems we should never call jffs2_mark_node_obsolete() for - XATTR nodes.... yet. Make sure we notice if/when we change - that :) */ - BUG_ON(ic->class != RAWNODE_CLASS_INODE_CACHE); for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino)) ; *p = ref->next_in_ino; ref->next_in_ino = NULL; - if (ic->nodes == (void *)ic && ic->nlink == 0) - jffs2_del_ino_cache(c, ic); - + switch (ic->class) { +#ifdef CONFIG_JFFS2_FS_XATTR + case RAWNODE_CLASS_XATTR_DATUM: + jffs2_release_xattr_datum(c, (struct jffs2_xattr_datum *)ic); + break; + case RAWNODE_CLASS_XATTR_REF: + jffs2_release_xattr_ref(c, (struct jffs2_xattr_ref *)ic); + break; +#endif + default: + if (ic->nodes == (void *)ic && ic->nlink == 0) + jffs2_del_ino_cache(c, ic); + break; + } spin_unlock(&c->erase_completion_lock); } diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 61618080b86..79638f56c5e 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -317,20 +317,25 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc struct jffs2_summary *s) { struct jffs2_xattr_datum *xd; - uint32_t totlen, crc; + uint32_t xid, version, totlen, crc; int err; crc = crc32(0, rx, sizeof(struct jffs2_raw_xattr) - 4); if (crc != je32_to_cpu(rx->node_crc)) { - if (je32_to_cpu(rx->node_crc) != 0xffffffff) - JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", - ofs, je32_to_cpu(rx->node_crc), crc); + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rx->node_crc), crc); if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(rx->totlen)))) return err; return 0; } - totlen = PAD(sizeof(*rx) + rx->name_len + 1 + je16_to_cpu(rx->value_len)); + xid = je32_to_cpu(rx->xid); + version = je32_to_cpu(rx->version); + + totlen = sizeof(struct jffs2_raw_xattr); + if (version != XDATUM_DELETE_MARKER) + totlen += rx->name_len + 1 + je16_to_cpu(rx->value_len); + totlen = PAD(totlen); if (totlen != je32_to_cpu(rx->totlen)) { JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", ofs, je32_to_cpu(rx->totlen), totlen); @@ -339,22 +344,24 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc return 0; } - xd = jffs2_setup_xattr_datum(c, je32_to_cpu(rx->xid), je32_to_cpu(rx->version)); - if (IS_ERR(xd)) { - if (PTR_ERR(xd) == -EEXIST) { - if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rx->totlen))))) - return err; - return 0; - } + xd = jffs2_setup_xattr_datum(c, xid, version); + if (IS_ERR(xd)) return PTR_ERR(xd); - } - xd->xprefix = rx->xprefix; - xd->name_len = rx->name_len; - xd->value_len = je16_to_cpu(rx->value_len); - xd->data_crc = je32_to_cpu(rx->data_crc); - xd->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL); - /* FIXME */ xd->node->next_in_ino = (void *)xd; + if (xd->version > version) { + struct jffs2_raw_node_ref *raw + = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, NULL); + raw->next_in_ino = xd->node->next_in_ino; + xd->node->next_in_ino = raw; + } else { + xd->version = version; + xd->xprefix = rx->xprefix; + xd->name_len = rx->name_len; + xd->value_len = je16_to_cpu(rx->value_len); + xd->data_crc = je32_to_cpu(rx->data_crc); + + jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, totlen, (void *)xd); + } if (jffs2_sum_active()) jffs2_sum_add_xattr_mem(s, rx, ofs - jeb->offset); @@ -373,9 +380,8 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock crc = crc32(0, rr, sizeof(*rr) - 4); if (crc != je32_to_cpu(rr->node_crc)) { - if (je32_to_cpu(rr->node_crc) != 0xffffffff) - JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", - ofs, je32_to_cpu(rr->node_crc), crc); + JFFS2_WARNING("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + ofs, je32_to_cpu(rr->node_crc), crc); if ((err = jffs2_scan_dirty_space(c, jeb, PAD(je32_to_cpu(rr->totlen))))) return err; return 0; @@ -395,6 +401,7 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock return -ENOMEM; /* BEFORE jffs2_build_xattr_subsystem() called, + * and AFTER xattr_ref is marked as a dead xref, * ref->xid is used to store 32bit xid, xd is not used * ref->ino is used to store 32bit inode-number, ic is not used * Thoes variables are declared as union, thus using those @@ -404,11 +411,13 @@ static int jffs2_scan_xref_node(struct jffs2_sb_info *c, struct jffs2_eraseblock */ ref->ino = je32_to_cpu(rr->ino); ref->xid = je32_to_cpu(rr->xid); + ref->xseqno = je32_to_cpu(rr->xseqno); + if (ref->xseqno > c->highest_xseqno) + c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); ref->next = c->xref_temp; c->xref_temp = ref; - ref->node = jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), NULL); - /* FIXME */ ref->node->next_in_ino = (void *)ref; + jffs2_link_node_ref(c, jeb, ofs | REF_PRISTINE, PAD(je32_to_cpu(rr->totlen)), (void *)ref); if (jffs2_sum_active()) jffs2_sum_add_xref_mem(s, rr, ofs - jeb->offset); diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index 0b02fc79e4d..c430f1d217e 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -310,8 +310,6 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, #ifdef CONFIG_JFFS2_FS_XATTR case JFFS2_NODETYPE_XATTR: { struct jffs2_sum_xattr_mem *temp; - if (je32_to_cpu(node->x.version) == 0xffffffff) - return 0; temp = kmalloc(sizeof(struct jffs2_sum_xattr_mem), GFP_KERNEL); if (!temp) goto no_mem; @@ -327,10 +325,6 @@ int jffs2_sum_add_kvec(struct jffs2_sb_info *c, const struct kvec *invecs, } case JFFS2_NODETYPE_XREF: { struct jffs2_sum_xref_mem *temp; - - if (je32_to_cpu(node->r.ino) == 0xffffffff - && je32_to_cpu(node->r.xid) == 0xffffffff) - return 0; temp = kmalloc(sizeof(struct jffs2_sum_xref_mem), GFP_KERNEL); if (!temp) goto no_mem; @@ -483,22 +477,20 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras xd = jffs2_setup_xattr_datum(c, je32_to_cpu(spx->xid), je32_to_cpu(spx->version)); - if (IS_ERR(xd)) { - if (PTR_ERR(xd) == -EEXIST) { - /* a newer version of xd exists */ - if ((err = jffs2_scan_dirty_space(c, jeb, je32_to_cpu(spx->totlen)))) - return err; - sp += JFFS2_SUMMARY_XATTR_SIZE; - break; - } - JFFS2_NOTICE("allocation of xattr_datum failed\n"); + if (IS_ERR(xd)) return PTR_ERR(xd); + if (xd->version > je32_to_cpu(spx->version)) { + /* node is not the newest one */ + struct jffs2_raw_node_ref *raw + = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spx->totlen)), NULL); + raw->next_in_ino = xd->node->next_in_ino; + xd->node->next_in_ino = raw; + } else { + xd->version = je32_to_cpu(spx->version); + sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, + PAD(je32_to_cpu(spx->totlen)), (void *)xd); } - - xd->node = sum_link_node_ref(c, jeb, je32_to_cpu(spx->offset) | REF_UNCHECKED, - PAD(je32_to_cpu(spx->totlen)), NULL); - /* FIXME */ xd->node->next_in_ino = (void *)xd; - *pseudo_random += je32_to_cpu(spx->xid); sp += JFFS2_SUMMARY_XATTR_SIZE; @@ -519,14 +511,11 @@ static int jffs2_sum_process_sum_data(struct jffs2_sb_info *c, struct jffs2_eras JFFS2_NOTICE("allocation of xattr_datum failed\n"); return -ENOMEM; } - ref->ino = 0xfffffffe; - ref->xid = 0xfffffffd; ref->next = c->xref_temp; c->xref_temp = ref; - ref->node = sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED, - PAD(sizeof(struct jffs2_raw_xref)), NULL); - /* FIXME */ ref->node->next_in_ino = (void *)ref; + sum_link_node_ref(c, jeb, je32_to_cpu(spr->offset) | REF_UNCHECKED, + PAD(sizeof(struct jffs2_raw_xref)), (void *)ref); *pseudo_random += ref->node->flash_offset; sp += JFFS2_SUMMARY_XREF_SIZE; diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 2d82e250be3..03871ab7c26 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -23,18 +23,15 @@ * xattr_datum_hashkey(xprefix, xname, xvalue, xsize) * is used to calcurate xdatum hashkey. The reminder of hashkey into XATTRINDEX_HASHSIZE is * the index of the xattr name/value pair cache (c->xattrindex). + * is_xattr_datum_unchecked(c, xd) + * returns 1, if xdatum contains any unchecked raw nodes. if all raw nodes are not + * unchecked, it returns 0. * unload_xattr_datum(c, xd) * is used to release xattr name/value pair and detach from c->xattrindex. * reclaim_xattr_datum(c) * is used to reclaim xattr name/value pairs on the xattr name/value pair cache when * memory usage by cache is over c->xdatum_mem_threshold. Currentry, this threshold * is hard coded as 32KiB. - * delete_xattr_datum_node(c, xd) - * is used to delete a jffs2 node is dominated by xdatum. When EBS(Erase Block Summary) is - * enabled, it overwrites the obsolete node by myself. - * delete_xattr_datum(c, xd) - * is used to delete jffs2_xattr_datum object. It must be called with 0-value of reference - * counter. (It means how many jffs2_xattr_ref object refers this xdatum.) * do_verify_xattr_datum(c, xd) * is used to load the xdatum informations without name/value pair from the medium. * It's necessary once, because those informations are not collected during mounting @@ -53,8 +50,13 @@ * is used to write xdatum to medium. xd->version will be incremented. * create_xattr_datum(c, xprefix, xname, xvalue, xsize) * is used to create new xdatum and write to medium. + * delete_xattr_datum_delay(c, xd) + * is used to delete a xdatum without 'delete marker'. It has a possibility to detect + * orphan xdatum on next mounting. + * delete_xattr_datum(c, xd) + * is used to delete a xdatum with 'delete marker'. Calling jffs2_reserve_space() is + * necessary before this function. * -------------------------------------------------- */ - static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) { int name_len = strlen(xname); @@ -62,6 +64,22 @@ static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char * return crc32(xprefix, xname, name_len) ^ crc32(xprefix, xvalue, xsize); } +static int is_xattr_datum_unchecked(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + struct jffs2_raw_node_ref *raw; + int rc = 0; + + spin_lock(&c->erase_completion_lock); + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + if (ref_flags(raw) == REF_UNCHECKED) { + rc = 1; + break; + } + } + spin_unlock(&c->erase_completion_lock); + return rc; +} + static void unload_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ @@ -107,80 +125,39 @@ static void reclaim_xattr_datum(struct jffs2_sb_info *c) before, c->xdatum_mem_usage, before - c->xdatum_mem_usage); } -static void delete_xattr_datum_node(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) -{ - /* must be called under down_write(xattr_sem) */ - struct jffs2_raw_xattr rx; - size_t length; - int rc; - - if (!xd->node) { - JFFS2_WARNING("xdatum (xid=%u) is removed twice.\n", xd->xid); - return; - } - if (jffs2_sum_active()) { - memset(&rx, 0xff, sizeof(struct jffs2_raw_xattr)); - rc = jffs2_flash_read(c, ref_offset(xd->node), - sizeof(struct jffs2_unknown_node), - &length, (char *)&rx); - if (rc || length != sizeof(struct jffs2_unknown_node)) { - JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n", - rc, sizeof(struct jffs2_unknown_node), - length, ref_offset(xd->node)); - } - rc = jffs2_flash_write(c, ref_offset(xd->node), sizeof(rx), - &length, (char *)&rx); - if (rc || length != sizeof(struct jffs2_raw_xattr)) { - JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu ar %#08x\n", - rc, sizeof(rx), length, ref_offset(xd->node)); - } - } - spin_lock(&c->erase_completion_lock); - xd->node->next_in_ino = NULL; - spin_unlock(&c->erase_completion_lock); - jffs2_mark_node_obsolete(c, xd->node); - xd->node = NULL; -} - -static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) -{ - /* must be called under down_write(xattr_sem) */ - BUG_ON(xd->refcnt); - - unload_xattr_datum(c, xd); - if (xd->node) { - delete_xattr_datum_node(c, xd); - xd->node = NULL; - } - jffs2_free_xattr_datum(xd); -} - static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; struct jffs2_raw_xattr rx; size_t readlen; - uint32_t crc, totlen; + uint32_t crc, offset, totlen; int rc; - BUG_ON(!xd->node); - BUG_ON(ref_flags(xd->node) != REF_UNCHECKED); + spin_lock(&c->erase_completion_lock); + offset = ref_offset(xd->node); + if (ref_flags(xd->node) == REF_PRISTINE) + goto complete; + spin_unlock(&c->erase_completion_lock); - rc = jffs2_flash_read(c, ref_offset(xd->node), sizeof(rx), &readlen, (char *)&rx); + rc = jffs2_flash_read(c, offset, sizeof(rx), &readlen, (char *)&rx); if (rc || readlen != sizeof(rx)) { JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n", - rc, sizeof(rx), readlen, ref_offset(xd->node)); + rc, sizeof(rx), readlen, offset); return rc ? rc : -EIO; } crc = crc32(0, &rx, sizeof(rx) - 4); if (crc != je32_to_cpu(rx.node_crc)) { - if (je32_to_cpu(rx.node_crc) != 0xffffffff) - JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", - ref_offset(xd->node), je32_to_cpu(rx.hdr_crc), crc); + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + offset, je32_to_cpu(rx.hdr_crc), crc); + xd->flags |= JFFS2_XFLAGS_INVALID; return EIO; } - totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); + totlen = sizeof(rx); + if (xd->version != XDATUM_DELETE_MARKER) + totlen += rx.name_len + 1 + je16_to_cpu(rx.value_len); + totlen = PAD(totlen); if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK || je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR || je32_to_cpu(rx.totlen) != totlen @@ -188,11 +165,12 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat || je32_to_cpu(rx.version) != xd->version) { JFFS2_ERROR("inconsistent xdatum at %#08x, magic=%#04x/%#04x, " "nodetype=%#04x/%#04x, totlen=%u/%u, xid=%u/%u, version=%u/%u\n", - ref_offset(xd->node), je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK, + offset, je16_to_cpu(rx.magic), JFFS2_MAGIC_BITMASK, je16_to_cpu(rx.nodetype), JFFS2_NODETYPE_XATTR, je32_to_cpu(rx.totlen), totlen, je32_to_cpu(rx.xid), xd->xid, je32_to_cpu(rx.version), xd->version); + xd->flags |= JFFS2_XFLAGS_INVALID; return EIO; } xd->xprefix = rx.xprefix; @@ -200,14 +178,17 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat xd->value_len = je16_to_cpu(rx.value_len); xd->data_crc = je32_to_cpu(rx.data_crc); - /* This JFFS2_NODETYPE_XATTR node is checked */ - jeb = &c->blocks[ref_offset(xd->node) / c->sector_size]; - totlen = PAD(je32_to_cpu(rx.totlen)); - spin_lock(&c->erase_completion_lock); - c->unchecked_size -= totlen; c->used_size += totlen; - jeb->unchecked_size -= totlen; jeb->used_size += totlen; - xd->node->flash_offset = ref_offset(xd->node) | REF_PRISTINE; + complete: + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + if (ref_flags(raw) == REF_UNCHECKED) { + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + } + raw->flash_offset = ref_offset(raw) | ((xd->node==raw) ? REF_PRISTINE : REF_NORMAL); + } spin_unlock(&c->erase_completion_lock); /* unchecked xdatum is chained with c->xattr_unchecked */ @@ -227,7 +208,6 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum uint32_t crc, length; int i, ret, retry = 0; - BUG_ON(!xd->node); BUG_ON(ref_flags(xd->node) != REF_PRISTINE); BUG_ON(!list_empty(&xd->xindex)); retry: @@ -253,6 +233,7 @@ static int do_load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum " at %#08x, read: 0x%08x calculated: 0x%08x\n", ref_offset(xd->node), xd->data_crc, crc); kfree(data); + xd->flags |= JFFS2_XFLAGS_INVALID; return EIO; } @@ -286,16 +267,13 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x * rc > 0 : Unrecoverable error, this node should be deleted. */ int rc = 0; - BUG_ON(xd->xname); - if (!xd->node) + + if (xd->xname) + return 0; + if (xd->flags & JFFS2_XFLAGS_INVALID) return EIO; - if (unlikely(ref_flags(xd->node) != REF_PRISTINE)) { + if (unlikely(is_xattr_datum_unchecked(c, xd))) rc = do_verify_xattr_datum(c, xd); - if (rc > 0) { - list_del_init(&xd->xindex); - delete_xattr_datum_node(c, xd); - } - } if (!rc) rc = do_load_xattr_datum(c, xd); return rc; @@ -304,36 +282,43 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ - struct jffs2_raw_node_ref *raw; struct jffs2_raw_xattr rx; struct kvec vecs[2]; size_t length; - int rc, totlen; + int rc, totlen, nvecs = 1; uint32_t phys_ofs = write_ofs(c); - BUG_ON(!xd->xname); + BUG_ON(is_xattr_datum_dead(xd) || (xd->flags & JFFS2_XFLAGS_INVALID) + ? !!xd->xname : !xd->xname); vecs[0].iov_base = ℞ - vecs[0].iov_len = PAD(sizeof(rx)); - vecs[1].iov_base = xd->xname; - vecs[1].iov_len = xd->name_len + 1 + xd->value_len; - totlen = vecs[0].iov_len + vecs[1].iov_len; - + vecs[0].iov_len = totlen = sizeof(rx); + if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) { + nvecs++; + vecs[1].iov_base = xd->xname; + vecs[1].iov_len = xd->name_len + 1 + xd->value_len; + totlen += vecs[1].iov_len; + } /* Setup raw-xattr */ + memset(&rx, 0, sizeof(rx)); rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); rx.nodetype = cpu_to_je16(JFFS2_NODETYPE_XATTR); rx.totlen = cpu_to_je32(PAD(totlen)); rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); rx.xid = cpu_to_je32(xd->xid); - rx.version = cpu_to_je32(++xd->version); - rx.xprefix = xd->xprefix; - rx.name_len = xd->name_len; - rx.value_len = cpu_to_je16(xd->value_len); - rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); + if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) { + rx.version = cpu_to_je32(++xd->version); + rx.xprefix = xd->xprefix; + rx.name_len = xd->name_len; + rx.value_len = cpu_to_je16(xd->value_len); + rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); + } else { + rx.version = cpu_to_je32(XDATUM_DELETE_MARKER); + } rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4)); - rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); + rc = jffs2_flash_writev(c, vecs, nvecs, phys_ofs, &length, 0); if (rc || totlen != length) { JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%zu, at %#08x\n", rc, totlen, length, phys_ofs); @@ -343,14 +328,8 @@ static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x return rc; } - /* success */ - raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), NULL); - /* FIXME */ raw->next_in_ino = (void *)xd; - - if (xd->node) - delete_xattr_datum_node(c, xd); - xd->node = raw; + jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(totlen), (void *)xd); dbg_xattr("success on saving xdatum (xid=%u, version=%u, xprefix=%u, xname='%s')\n", xd->xid, xd->version, xd->xprefix, xd->xname); @@ -426,6 +405,39 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, return xd; } +static void delete_xattr_datum_delay(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under down_write(xattr_sem) */ + BUG_ON(xd->refcnt); + + unload_xattr_datum(c, xd); + set_xattr_datum_dead(xd); + spin_lock(&c->erase_completion_lock); + list_add(&xd->xindex, &c->xattr_dead_list); + spin_unlock(&c->erase_completion_lock); + JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. " + "An orphan xdatum may be detected on next mounting.\n", xd->xid); +} + +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */ + int rc; + BUG_ON(xd->refcnt); + + unload_xattr_datum(c, xd); + set_xattr_datum_dead(xd); + rc = save_xattr_datum(c, xd); + if (rc) { + JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. " + "An orphan xdatum may be detected on next mounting.\n", + xd->xid); + } + spin_lock(&c->erase_completion_lock); + list_add(&xd->xindex, &c->xattr_dead_list); + spin_unlock(&c->erase_completion_lock); +} + /* -------- xref related functions ------------------ * verify_xattr_ref(c, ref) * is used to load xref information from medium. Because summary data does not @@ -450,25 +462,29 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; struct jffs2_raw_xref rr; size_t readlen; - uint32_t crc, totlen; + uint32_t crc, offset, totlen; int rc; - BUG_ON(ref_flags(ref->node) != REF_UNCHECKED); + spin_lock(&c->erase_completion_lock); + if (ref_flags(ref->node) != REF_UNCHECKED) + goto complete; + offset = ref_offset(ref->node); + spin_unlock(&c->erase_completion_lock); - rc = jffs2_flash_read(c, ref_offset(ref->node), sizeof(rr), &readlen, (char *)&rr); + rc = jffs2_flash_read(c, offset, sizeof(rr), &readlen, (char *)&rr); if (rc || sizeof(rr) != readlen) { JFFS2_WARNING("jffs2_flash_read()=%d, req=%zu, read=%zu, at %#08x\n", - rc, sizeof(rr), readlen, ref_offset(ref->node)); + rc, sizeof(rr), readlen, offset); return rc ? rc : -EIO; } /* obsolete node */ crc = crc32(0, &rr, sizeof(rr) - 4); if (crc != je32_to_cpu(rr.node_crc)) { - if (je32_to_cpu(rr.node_crc) != 0xffffffff) - JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", - ref_offset(ref->node), je32_to_cpu(rr.node_crc), crc); + JFFS2_ERROR("node CRC failed at %#08x, read=%#08x, calc=%#08x\n", + offset, je32_to_cpu(rr.node_crc), crc); return EIO; } if (je16_to_cpu(rr.magic) != JFFS2_MAGIC_BITMASK @@ -476,22 +492,28 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref || je32_to_cpu(rr.totlen) != PAD(sizeof(rr))) { JFFS2_ERROR("inconsistent xref at %#08x, magic=%#04x/%#04x, " "nodetype=%#04x/%#04x, totlen=%u/%zu\n", - ref_offset(ref->node), je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, + offset, je16_to_cpu(rr.magic), JFFS2_MAGIC_BITMASK, je16_to_cpu(rr.nodetype), JFFS2_NODETYPE_XREF, je32_to_cpu(rr.totlen), PAD(sizeof(rr))); return EIO; } ref->ino = je32_to_cpu(rr.ino); ref->xid = je32_to_cpu(rr.xid); - - /* fixup superblock/eraseblock info */ - jeb = &c->blocks[ref_offset(ref->node) / c->sector_size]; - totlen = PAD(sizeof(rr)); + ref->xseqno = je32_to_cpu(rr.xseqno); + if (ref->xseqno > c->highest_xseqno) + c->highest_xseqno = (ref->xseqno & ~XREF_DELETE_MARKER); spin_lock(&c->erase_completion_lock); - c->unchecked_size -= totlen; c->used_size += totlen; - jeb->unchecked_size -= totlen; jeb->used_size += totlen; - ref->node->flash_offset = ref_offset(ref->node) | REF_PRISTINE; + complete: + for (raw=ref->node; raw != (void *)ref; raw=raw->next_in_ino) { + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + if (ref_flags(raw) == REF_UNCHECKED) { + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + } + raw->flash_offset = ref_offset(raw) | ((ref->node==raw) ? REF_PRISTINE : REF_NORMAL); + } spin_unlock(&c->erase_completion_lock); dbg_xattr("success on verifying xref (ino=%u, xid=%u) at %#08x\n", @@ -499,58 +521,12 @@ static int verify_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref return 0; } -static void delete_xattr_ref_node(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) -{ - struct jffs2_raw_xref rr; - size_t length; - int rc; - - if (jffs2_sum_active()) { - memset(&rr, 0xff, sizeof(rr)); - rc = jffs2_flash_read(c, ref_offset(ref->node), - sizeof(struct jffs2_unknown_node), - &length, (char *)&rr); - if (rc || length != sizeof(struct jffs2_unknown_node)) { - JFFS2_ERROR("jffs2_flash_read()=%d, req=%zu, read=%zu at %#08x\n", - rc, sizeof(struct jffs2_unknown_node), - length, ref_offset(ref->node)); - } - rc = jffs2_flash_write(c, ref_offset(ref->node), sizeof(rr), - &length, (char *)&rr); - if (rc || length != sizeof(struct jffs2_raw_xref)) { - JFFS2_ERROR("jffs2_flash_write()=%d, req=%zu, wrote=%zu at %#08x\n", - rc, sizeof(rr), length, ref_offset(ref->node)); - } - } - spin_lock(&c->erase_completion_lock); - ref->node->next_in_ino = NULL; - spin_unlock(&c->erase_completion_lock); - jffs2_mark_node_obsolete(c, ref->node); - ref->node = NULL; -} - -static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) -{ - /* must be called under down_write(xattr_sem) */ - struct jffs2_xattr_datum *xd; - - BUG_ON(!ref->node); - delete_xattr_ref_node(c, ref); - - xd = ref->xd; - xd->refcnt--; - if (!xd->refcnt) - delete_xattr_datum(c, xd); - jffs2_free_xattr_ref(ref); -} - static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { /* must be called under down_write(xattr_sem) */ - struct jffs2_raw_node_ref *raw; struct jffs2_raw_xref rr; size_t length; - uint32_t phys_ofs = write_ofs(c); + uint32_t xseqno, phys_ofs = write_ofs(c); int ret; rr.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -558,8 +534,16 @@ static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) rr.totlen = cpu_to_je32(PAD(sizeof(rr))); rr.hdr_crc = cpu_to_je32(crc32(0, &rr, sizeof(struct jffs2_unknown_node) - 4)); - rr.ino = cpu_to_je32(ref->ic->ino); - rr.xid = cpu_to_je32(ref->xd->xid); + xseqno = (c->highest_xseqno += 2); + if (is_xattr_ref_dead(ref)) { + xseqno |= XREF_DELETE_MARKER; + rr.ino = cpu_to_je32(ref->ino); + rr.xid = cpu_to_je32(ref->xid); + } else { + rr.ino = cpu_to_je32(ref->ic->ino); + rr.xid = cpu_to_je32(ref->xd->xid); + } + rr.xseqno = cpu_to_je32(xseqno); rr.node_crc = cpu_to_je32(crc32(0, &rr, sizeof(rr) - 4)); ret = jffs2_flash_write(c, phys_ofs, sizeof(rr), &length, (char *)&rr); @@ -572,12 +556,9 @@ static int save_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) return ret; } - - raw = jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), NULL); - /* FIXME */ raw->next_in_ino = (void *)ref; - if (ref->node) - delete_xattr_ref_node(c, ref); - ref->node = raw; + /* success */ + ref->xseqno = xseqno; + jffs2_add_physical_node_ref(c, phys_ofs | REF_PRISTINE, PAD(sizeof(rr)), (void *)ref); dbg_xattr("success on saving xref (ino=%u, xid=%u)\n", ref->ic->ino, ref->xd->xid); @@ -610,22 +591,116 @@ static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct return ref; /* success */ } +static void delete_xattr_ref_delay(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under down_write(xattr_sem) */ + struct jffs2_xattr_datum *xd; + + set_xattr_ref_dead(ref); + xd = ref->xd; + 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); + + JFFS2_NOTICE("xref(ino=%u, xid=%u) was removed without delete marker. " + "An orphan xref may be detected on next mounting.\n", + ref->ino, ref->xid); + + if (!--xd->refcnt) + delete_xattr_datum_delay(c, xd); +} + +static int delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, int enforce) +{ + /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */ + struct jffs2_inode_cache *ic; + struct jffs2_xattr_datum *xd; + uint32_t length; + int rc; + + set_xattr_ref_dead(ref); + ic = ref->ic; + xd = ref->xd; + ref->ino = ic->ino; + ref->xid = xd->xid; + rc = save_xattr_ref(c, ref); + if (rc) { + if (!enforce) { + clr_xattr_ref_dead(ref); + ref->ic = ic; + ref->xd = xd; + return rc; + } + JFFS2_WARNING("could not write delete marker of xref(ino=%u, xid=%u). " + "An orphan xref may be detected on next mounting.\n", + ref->ic->ino, 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); + + xd->refcnt--; + if (xd->refcnt) + return 0; + + /* delete xdatum */ + unload_xattr_datum(c, xd); + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xattr)), &length, + ALLOC_DELETION, JFFS2_SUMMARY_XATTR_SIZE); + if (rc) { + down(&c->alloc_sem); + down_write(&c->xattr_sem); + delete_xattr_datum_delay(c, xd); + } else { + down_write(&c->xattr_sem); + delete_xattr_datum(c, xd); + } + return 0; +} + void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { /* It's called from jffs2_clear_inode() on inode removing. When an inode with XATTR is removed, those XATTRs must be removed. */ - struct jffs2_xattr_ref *ref, *_ref; + struct jffs2_xattr_ref *ref; + uint32_t length; + int rc, retry; if (!ic || ic->nlink > 0) return; + down_read(&c->xattr_sem); + if (!ic->xref) { + up_read(&c->xattr_sem); + return; + } + up_read(&c->xattr_sem); + retry: + rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xref)), &length, + ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE); down_write(&c->xattr_sem); - for (ref = ic->xref; ref; ref = _ref) { - _ref = ref->next; - delete_xattr_ref(c, ref); + if (ic->xref) { + ref = ic->xref; + ic->xref = ref->next; + if (rc) { + delete_xattr_ref_delay(c, ref); + } else { + delete_xattr_ref(c, ref, 1); + } } - ic->xref = NULL; + retry = ic->xref ? 1 : 0; up_write(&c->xattr_sem); + if (!rc) + jffs2_complete_reservation(c); + if (retry) + goto retry; } void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) @@ -655,7 +730,7 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac * duplicate name/value pairs. If duplicate name/value pair would be found, * one will be removed. */ - struct jffs2_xattr_ref *ref, *cmp, **pref; + struct jffs2_xattr_ref *ref, *cmp, **pref, **pcmp; int rc = 0; if (likely(ic->flags & INO_FLAGS_XATTR_CHECKED)) @@ -668,27 +743,32 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac rc = load_xattr_datum(c, ref->xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref(c, ref); + delete_xattr_ref_delay(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; } - for (cmp=ref->next, pref=&ref->next; cmp; pref=&cmp->next, cmp=cmp->next) { + for (cmp=ref->next, pcmp=&ref->next; cmp; pcmp=&cmp->next, cmp=cmp->next) { if (!cmp->xd->xname) { ref->xd->flags |= JFFS2_XFLAGS_BIND; rc = load_xattr_datum(c, cmp->xd); ref->xd->flags &= ~JFFS2_XFLAGS_BIND; if (unlikely(rc > 0)) { - *pref = cmp->next; - delete_xattr_ref(c, cmp); + *pcmp = cmp->next; + delete_xattr_ref_delay(c, cmp); goto retry; } else if (unlikely(rc < 0)) goto out; } if (ref->xd->xprefix == cmp->xd->xprefix && !strcmp(ref->xd->xname, cmp->xd->xname)) { - *pref = cmp->next; - delete_xattr_ref(c, cmp); + if (ref->xseqno > cmp->xseqno) { + *pcmp = cmp->next; + delete_xattr_ref_delay(c, cmp); + } else { + *pref = ref->next; + delete_xattr_ref_delay(c, ref); + } goto retry; } } @@ -719,9 +799,13 @@ void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c) for (i=0; i < XATTRINDEX_HASHSIZE; i++) INIT_LIST_HEAD(&c->xattrindex[i]); INIT_LIST_HEAD(&c->xattr_unchecked); + INIT_LIST_HEAD(&c->xattr_dead_list); + c->xref_dead_list = NULL; c->xref_temp = NULL; init_rwsem(&c->xattr_sem); + c->highest_xid = 0; + c->highest_xseqno = 0; c->xdatum_mem_usage = 0; c->xdatum_mem_threshold = 32 * 1024; /* Default 32KB */ } @@ -751,7 +835,11 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) _ref = ref->next; jffs2_free_xattr_ref(ref); } - c->xref_temp = NULL; + + for (ref=c->xref_dead_list; ref; ref = _ref) { + _ref = ref->next; + jffs2_free_xattr_ref(ref); + } for (i=0; i < XATTRINDEX_HASHSIZE; i++) { list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { @@ -761,64 +849,120 @@ void jffs2_clear_xattr_subsystem(struct jffs2_sb_info *c) jffs2_free_xattr_datum(xd); } } + + list_for_each_entry_safe(xd, _xd, &c->xattr_dead_list, xindex) { + list_del(&xd->xindex); + jffs2_free_xattr_datum(xd); + } } +#define XREF_TMPHASH_SIZE (128) void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) { struct jffs2_xattr_ref *ref, *_ref; + struct jffs2_xattr_ref *xref_tmphash[XREF_TMPHASH_SIZE]; struct jffs2_xattr_datum *xd, *_xd; struct jffs2_inode_cache *ic; - int i, xdatum_count =0, xdatum_unchecked_count = 0, xref_count = 0; + struct jffs2_raw_node_ref *raw; + int i, xdatum_count = 0, xdatum_unchecked_count = 0, xref_count = 0; BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); + /* Phase.1 : Drop dead xdatum */ + for (i=0; i < XATTRINDEX_HASHSIZE; i++) { + list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + BUG_ON(xd->node == (void *)xd); + if (is_xattr_datum_dead(xd)) { + list_del_init(&xd->xindex); + list_add(&xd->xindex, &c->xattr_unchecked); + } + } + } - /* Phase.1 */ + /* Phase.2 : Merge same xref */ + for (i=0; i < XREF_TMPHASH_SIZE; i++) + xref_tmphash[i] = NULL; for (ref=c->xref_temp; ref; ref=_ref) { + struct jffs2_xattr_ref *tmp; + _ref = ref->next; - /* checking REF_UNCHECKED nodes */ if (ref_flags(ref->node) != REF_PRISTINE) { if (verify_xattr_ref(c, ref)) { - delete_xattr_ref_node(c, ref); + BUG_ON(ref->node->next_in_ino != (void *)ref); + ref->node->next_in_ino = NULL; + jffs2_mark_node_obsolete(c, ref->node); jffs2_free_xattr_ref(ref); continue; } } - /* At this point, ref->xid and ref->ino contain XID and inode number. - ref->xd and ref->ic are not valid yet. */ - xd = jffs2_find_xattr_datum(c, ref->xid); - ic = jffs2_get_ino_cache(c, ref->ino); - if (!xd || !ic) { - if (ref_flags(ref->node) != REF_UNCHECKED) - JFFS2_WARNING("xref(ino=%u, xid=%u) is orphan. \n", - ref->ino, ref->xid); - delete_xattr_ref_node(c, ref); + + i = (ref->ino ^ ref->xid) % XREF_TMPHASH_SIZE; + for (tmp=xref_tmphash[i]; tmp; tmp=tmp->next) { + if (tmp->ino == ref->ino && tmp->xid == ref->xid) + break; + } + if (tmp) { + raw = ref->node; + if (ref->xseqno > tmp->xseqno) { + tmp->xseqno = ref->xseqno; + raw->next_in_ino = tmp->node; + tmp->node = raw; + } else { + raw->next_in_ino = tmp->node->next_in_ino; + tmp->node->next_in_ino = raw; + } jffs2_free_xattr_ref(ref); continue; + } else { + ref->next = xref_tmphash[i]; + xref_tmphash[i] = ref; } - ref->xd = xd; - ref->ic = ic; - xd->refcnt++; - ref->next = ic->xref; - ic->xref = ref; - xref_count++; } c->xref_temp = NULL; - /* After this, ref->xid/ino are NEVER used. */ - /* Phase.2 */ + /* Phase.3 : Bind xref with inode_cache and xattr_datum */ + for (i=0; i < XREF_TMPHASH_SIZE; i++) { + for (ref=xref_tmphash[i]; ref; ref=_ref) { + _ref = ref->next; + if (is_xattr_ref_dead(ref)) { + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + continue; + } + /* At this point, ref->xid and ref->ino contain XID and inode number. + ref->xd and ref->ic are not valid yet. */ + xd = jffs2_find_xattr_datum(c, ref->xid); + ic = jffs2_get_ino_cache(c, ref->ino); + if (!xd || !ic) { + JFFS2_WARNING("xref(ino=%u, xid=%u, xseqno=%u) is orphan. \n", + ref->ino, ref->xid, ref->xseqno); + set_xattr_ref_dead(ref); + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + continue; + } + ref->xd = xd; + ref->ic = ic; + xd->refcnt++; + ref->next = ic->xref; + ic->xref = ref; + xref_count++; + } + } + + /* Phase.4 : Link unchecked xdatum to xattr_unchecked list */ for (i=0; i < XATTRINDEX_HASHSIZE; i++) { list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { list_del_init(&xd->xindex); if (!xd->refcnt) { - if (ref_flags(xd->node) != REF_UNCHECKED) - JFFS2_WARNING("orphan xdatum(xid=%u, version=%u) at %#08x\n", - xd->xid, xd->version, ref_offset(xd->node)); - delete_xattr_datum(c, xd); + JFFS2_WARNING("orphan xdatum(xid=%u, version=%u)\n", + xd->xid, xd->version); + set_xattr_datum_dead(xd); + list_add(&xd->xindex, &c->xattr_unchecked); continue; } - if (ref_flags(xd->node) != REF_PRISTINE) { - dbg_xattr("unchecked xdatum(xid=%u) at %#08x\n", - xd->xid, ref_offset(xd->node)); + if (is_xattr_datum_unchecked(c, xd)) { + dbg_xattr("unchecked xdatum(xid=%u, version=%u)\n", + xd->xid, xd->version); list_add(&xd->xindex, &c->xattr_unchecked); xdatum_unchecked_count++; } @@ -833,28 +977,18 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, uint32_t xid, uint32_t version) { - struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_xattr_datum *xd; - _xd = jffs2_find_xattr_datum(c, xid); - if (_xd) { - dbg_xattr("duplicate xdatum (xid=%u, version=%u/%u) at %#08x\n", - xid, version, _xd->version, ref_offset(_xd->node)); - if (version < _xd->version) - return ERR_PTR(-EEXIST); - } - xd = jffs2_alloc_xattr_datum(); - if (!xd) - return ERR_PTR(-ENOMEM); - xd->xid = xid; - xd->version = version; - if (xd->xid > c->highest_xid) - c->highest_xid = xd->xid; - list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]); - - if (_xd) { - list_del_init(&_xd->xindex); - delete_xattr_datum_node(c, _xd); - jffs2_free_xattr_datum(_xd); + xd = jffs2_find_xattr_datum(c, xid); + if (!xd) { + xd = jffs2_alloc_xattr_datum(); + if (!xd) + return ERR_PTR(-ENOMEM); + xd->xid = xid; + xd->version = version; + if (xd->xid > c->highest_xid) + c->highest_xid = xd->xid; + list_add_tail(&xd->xindex, &c->xattrindex[xid % XATTRINDEX_HASHSIZE]); } return xd; } @@ -945,7 +1079,7 @@ ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size) rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref(c, ref); + delete_xattr_ref_delay(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -1006,7 +1140,7 @@ int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref(c, ref); + delete_xattr_ref_delay(c, ref); goto retry; } else if (unlikely(rc < 0)) { goto out; @@ -1069,7 +1203,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref(c, ref); + delete_xattr_ref_delay(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -1081,8 +1215,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, } if (!buffer) { *pref = ref->next; - delete_xattr_ref(c, ref); - rc = 0; + rc = delete_xattr_ref(c, ref, 0); goto out; } goto found; @@ -1094,7 +1227,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, goto out; } if (!buffer) { - rc = -EINVAL; + rc = -ENODATA; goto out; } found: @@ -1110,16 +1243,15 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, request = PAD(sizeof(struct jffs2_raw_xref)); rc = jffs2_reserve_space(c, request, &length, ALLOC_NORMAL, JFFS2_SUMMARY_XREF_SIZE); + down_write(&c->xattr_sem); if (rc) { JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); - down_write(&c->xattr_sem); xd->refcnt--; if (!xd->refcnt) - delete_xattr_datum(c, xd); + delete_xattr_datum_delay(c, xd); up_write(&c->xattr_sem); return rc; } - down_write(&c->xattr_sem); if (ref) *pref = ref->next; newref = create_xattr_ref(c, ic, xd); @@ -1131,9 +1263,21 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, rc = PTR_ERR(newref); xd->refcnt--; if (!xd->refcnt) - delete_xattr_datum(c, xd); + delete_xattr_datum_delay(c, xd); } else if (ref) { - delete_xattr_ref(c, ref); + up_write(&c->xattr_sem); + jffs2_complete_reservation(c); + + rc = jffs2_reserve_space(c, request, &length, + ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE); + down_write(&c->xattr_sem); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); + delete_xattr_ref_delay(c, ref); + up_write(&c->xattr_sem); + return 0; + } + delete_xattr_ref(c, ref, 1); } out: up_write(&c->xattr_sem); @@ -1142,38 +1286,37 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, } /* -------- garbage collector functions ------------- - * jffs2_garbage_collect_xattr_datum(c, xd) + * jffs2_garbage_collect_xattr_datum(c, xd, raw) * is used to move xdatum into new node. - * jffs2_garbage_collect_xattr_ref(c, ref) + * jffs2_garbage_collect_xattr_ref(c, ref, raw) * is used to move xref into new node. * jffs2_verify_xattr(c) * is used to call do_verify_xattr_datum() before garbage collecting. * -------------------------------------------------- */ -int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, + struct jffs2_raw_node_ref *raw) { uint32_t totlen, length, old_ofs; - int rc = -EINVAL; + int rc = 0; down_write(&c->xattr_sem); - BUG_ON(!xd->node); + if (xd->node != raw) + goto out; + if (is_xattr_datum_dead(xd) && (raw->next_in_ino == (void *)xd)) + goto out; old_ofs = ref_offset(xd->node); totlen = ref_totlen(c, c->gcblock, xd->node); - if (totlen < sizeof(struct jffs2_raw_xattr)) - goto out; - if (!xd->xname) { + if (!is_xattr_datum_dead(xd)) { rc = load_xattr_datum(c, xd); - if (unlikely(rc > 0)) { - delete_xattr_datum_node(c, xd); - rc = 0; - goto out; - } else if (unlikely(rc < 0)) + if (unlikely(rc < 0)) goto out; } + rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XATTR_SIZE); - if (rc || length < totlen) { - JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, totlen); + if (rc) { + JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u\n", rc, totlen); rc = rc ? rc : -EBADFD; goto out; } @@ -1182,27 +1325,33 @@ int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xatt dbg_xattr("xdatum (xid=%u, version=%u) GC'ed from %#08x to %08x\n", xd->xid, xd->version, old_ofs, ref_offset(xd->node)); out: + if (!rc) + jffs2_mark_node_obsolete(c, raw); up_write(&c->xattr_sem); return rc; } -int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, + struct jffs2_raw_node_ref *raw) { uint32_t totlen, length, old_ofs; - int rc = -EINVAL; + int rc = 0; down_write(&c->xattr_sem); BUG_ON(!ref->node); + if (ref->node != raw) + goto out; + if (is_xattr_ref_dead(ref) && (raw->next_in_ino == (void *)ref)) + goto out; + old_ofs = ref_offset(ref->node); totlen = ref_totlen(c, c->gcblock, ref->node); - if (totlen != sizeof(struct jffs2_raw_xref)) - goto out; rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XREF_SIZE); - if (rc || length < totlen) { - JFFS2_WARNING("%s: jffs2_reserve_space() = %d, request = %u\n", + if (rc) { + JFFS2_WARNING("%s: jffs2_reserve_space_gc() = %d, request = %u\n", __FUNCTION__, rc, totlen); rc = rc ? rc : -EBADFD; goto out; @@ -1212,6 +1361,8 @@ int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ dbg_xattr("xref (ino=%u, xid=%u) GC'ed from %#08x to %08x\n", ref->ic->ino, ref->xd->xid, old_ofs, ref_offset(ref->node)); out: + if (!rc) + jffs2_mark_node_obsolete(c, raw); up_write(&c->xattr_sem); return rc; } @@ -1219,20 +1370,59 @@ int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ int jffs2_verify_xattr(struct jffs2_sb_info *c) { struct jffs2_xattr_datum *xd, *_xd; + struct jffs2_eraseblock *jeb; + struct jffs2_raw_node_ref *raw; + uint32_t totlen; int rc; down_write(&c->xattr_sem); list_for_each_entry_safe(xd, _xd, &c->xattr_unchecked, xindex) { rc = do_verify_xattr_datum(c, xd); - if (rc == 0) { - list_del_init(&xd->xindex); - break; - } else if (rc > 0) { - list_del_init(&xd->xindex); - delete_xattr_datum_node(c, xd); + if (rc < 0) + continue; + list_del_init(&xd->xindex); + spin_lock(&c->erase_completion_lock); + for (raw=xd->node; raw != (void *)xd; raw=raw->next_in_ino) { + if (ref_flags(raw) != REF_UNCHECKED) + continue; + jeb = &c->blocks[ref_offset(raw) / c->sector_size]; + totlen = PAD(ref_totlen(c, jeb, raw)); + c->unchecked_size -= totlen; c->used_size += totlen; + jeb->unchecked_size -= totlen; jeb->used_size += totlen; + raw->flash_offset = ref_offset(raw) + | ((xd->node == (void *)raw) ? REF_PRISTINE : REF_NORMAL); } + if (is_xattr_datum_dead(xd)) + list_add(&xd->xindex, &c->xattr_dead_list); + spin_unlock(&c->erase_completion_lock); } up_write(&c->xattr_sem); - return list_empty(&c->xattr_unchecked) ? 1 : 0; } + +void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +{ + /* must be called under spin_lock(&c->erase_completion_lock) */ + if (xd->node != (void *)xd) + return; + + list_del(&xd->xindex); + jffs2_free_xattr_datum(xd); +} + +void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +{ + /* must be called under spin_lock(&c->erase_completion_lock) */ + struct jffs2_xattr_ref *tmp, **ptmp; + + if (ref->node != (void *)ref) + return; + + for (tmp=c->xref_dead_list, ptmp=&c->xref_dead_list; tmp; ptmp=&tmp->next, tmp=tmp->next) { + if (ref == tmp) { + *ptmp = tmp->next; + jffs2_free_xattr_ref(ref); + break; + } + } +} diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 2c199856c58..06ab7b88021 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -16,6 +16,7 @@ #define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */ #define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */ +#define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */ struct jffs2_xattr_datum { @@ -23,7 +24,7 @@ struct jffs2_xattr_datum struct jffs2_raw_node_ref *node; uint8_t class; uint8_t flags; - uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */ + uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */ struct list_head xindex; /* chained from c->xattrindex[n] */ uint32_t refcnt; /* # of xattr_ref refers this */ @@ -47,6 +48,7 @@ struct jffs2_xattr_ref uint8_t flags; /* Currently unused */ u16 unused; + uint32_t xseqno; union { struct jffs2_inode_cache *ic; /* reference to jffs2_inode_cache */ uint32_t ino; /* only used in scanning/building */ @@ -58,6 +60,34 @@ struct jffs2_xattr_ref struct jffs2_xattr_ref *next; /* chained from ic->xref_list */ }; +#define XDATUM_DELETE_MARKER (0xffffffff) +#define XREF_DELETE_MARKER (0x00000001) +static inline int is_xattr_datum_dead(struct jffs2_xattr_datum *xd) +{ + return (xd->version == XDATUM_DELETE_MARKER); +} + +static inline void set_xattr_datum_dead(struct jffs2_xattr_datum *xd) +{ + xd->version = XDATUM_DELETE_MARKER; +} + +static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref) +{ + return ((ref->xseqno & XREF_DELETE_MARKER) != 0); +} + +static inline void set_xattr_ref_dead(struct jffs2_xattr_ref *ref) +{ + ref->xseqno |= XREF_DELETE_MARKER; +} + +static inline void clr_xattr_ref_dead(struct jffs2_xattr_ref *ref) +{ + ref->xseqno &= ~XREF_DELETE_MARKER; +} + + #ifdef CONFIG_JFFS2_FS_XATTR extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c); @@ -70,9 +100,13 @@ extern struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c extern void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); extern void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); -extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); -extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); +extern int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, + struct jffs2_raw_node_ref *raw); +extern int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, + struct jffs2_raw_node_ref *raw); extern int jffs2_verify_xattr(struct jffs2_sb_info *c); +extern void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd); +extern void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref); extern int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, char *buffer, size_t size); -- cgit v1.2.3 From a1ae76e96a18edf4b8a4d4e102762060c26d1c6a Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sun, 11 Jun 2006 10:45:22 +0900 Subject: [JFFS2][XATTR] Fix ACL bug when updating null xattr by null ACL. This patch enable to handle the case when updating null xattr by null ACL. When we try to set NULL into NULL xattr, xattr subsystem returns -ENODATA. This patch enables to handle this error code. [2/3] jffs2-xattr-v6-02-fix_posixacl_bug.patch Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/acl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs/jffs2') diff --git a/fs/jffs2/acl.c b/fs/jffs2/acl.c index 320dd48b834..9c2077e7e08 100644 --- a/fs/jffs2/acl.c +++ b/fs/jffs2/acl.c @@ -267,6 +267,8 @@ static int jffs2_set_acl(struct inode *inode, int type, struct posix_acl *acl) } rc = do_jffs2_setxattr(inode, xprefix, "", value, size, 0); + if (!value && rc == -ENODATA) + rc = 0; if (value) kfree(value); if (!rc) { -- cgit v1.2.3 From 8a13695cbe4e8311b363f9bd25162904b984ca74 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 24 Jun 2006 09:14:13 +0900 Subject: [JFFS2][XATTR] rid unnecessary writing of delete marker. In the followinf situation, an explicit delete marker is not necessary, because we can certainlly detect those obsolete xattr_datum or xattr_ref on next mounting. - When to delete xattr_datum node. - When to delete xattr_ref node on removing inode. - When to delete xattr_ref node on updating xattr. This patch rids writing delete marker in those situations. Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/scan.c | 6 +- fs/jffs2/xattr.c | 303 +++++++++++++++++++------------------------------------ fs/jffs2/xattr.h | 23 +---- 3 files changed, 105 insertions(+), 227 deletions(-) (limited to 'fs/jffs2') diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 79638f56c5e..2bfdc33752d 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -332,10 +332,8 @@ static int jffs2_scan_xattr_node(struct jffs2_sb_info *c, struct jffs2_erasebloc xid = je32_to_cpu(rx->xid); version = je32_to_cpu(rx->version); - totlen = sizeof(struct jffs2_raw_xattr); - if (version != XDATUM_DELETE_MARKER) - totlen += rx->name_len + 1 + je16_to_cpu(rx->value_len); - totlen = PAD(totlen); + totlen = PAD(sizeof(struct jffs2_raw_xattr) + + rx->name_len + 1 + je16_to_cpu(rx->value_len)); if (totlen != je32_to_cpu(rx->totlen)) { JFFS2_WARNING("node length mismatch at %#08x, read=%u, calc=%u\n", ofs, je32_to_cpu(rx->totlen), totlen); diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 03871ab7c26..7622f79cb5b 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -50,12 +50,9 @@ * is used to write xdatum to medium. xd->version will be incremented. * create_xattr_datum(c, xprefix, xname, xvalue, xsize) * is used to create new xdatum and write to medium. - * delete_xattr_datum_delay(c, xd) - * is used to delete a xdatum without 'delete marker'. It has a possibility to detect - * orphan xdatum on next mounting. * delete_xattr_datum(c, xd) - * is used to delete a xdatum with 'delete marker'. Calling jffs2_reserve_space() is - * necessary before this function. + * is used to delete a xdatum. It marks xd JFFS2_XFLAGS_DEAD, and allows + * GC to reclaim those physical nodes. * -------------------------------------------------- */ static uint32_t xattr_datum_hashkey(int xprefix, const char *xname, const char *xvalue, int xsize) { @@ -154,10 +151,7 @@ static int do_verify_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_dat xd->flags |= JFFS2_XFLAGS_INVALID; return EIO; } - totlen = sizeof(rx); - if (xd->version != XDATUM_DELETE_MARKER) - totlen += rx.name_len + 1 + je16_to_cpu(rx.value_len); - totlen = PAD(totlen); + totlen = PAD(sizeof(rx) + rx.name_len + 1 + je16_to_cpu(rx.value_len)); if (je16_to_cpu(rx.magic) != JFFS2_MAGIC_BITMASK || je16_to_cpu(rx.nodetype) != JFFS2_NODETYPE_XATTR || je32_to_cpu(rx.totlen) != totlen @@ -268,6 +262,7 @@ static int load_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x */ int rc = 0; + BUG_ON(xd->flags & JFFS2_XFLAGS_DEAD); if (xd->xname) return 0; if (xd->flags & JFFS2_XFLAGS_INVALID) @@ -285,20 +280,18 @@ static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x struct jffs2_raw_xattr rx; struct kvec vecs[2]; size_t length; - int rc, totlen, nvecs = 1; + int rc, totlen; uint32_t phys_ofs = write_ofs(c); - BUG_ON(is_xattr_datum_dead(xd) || (xd->flags & JFFS2_XFLAGS_INVALID) - ? !!xd->xname : !xd->xname); + BUG_ON(!xd->xname); + BUG_ON(xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID)); vecs[0].iov_base = ℞ - vecs[0].iov_len = totlen = sizeof(rx); - if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) { - nvecs++; - vecs[1].iov_base = xd->xname; - vecs[1].iov_len = xd->name_len + 1 + xd->value_len; - totlen += vecs[1].iov_len; - } + vecs[0].iov_len = sizeof(rx); + vecs[1].iov_base = xd->xname; + vecs[1].iov_len = xd->name_len + 1 + xd->value_len; + totlen = vecs[0].iov_len + vecs[1].iov_len; + /* Setup raw-xattr */ memset(&rx, 0, sizeof(rx)); rx.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); @@ -307,18 +300,14 @@ static int save_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *x rx.hdr_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_unknown_node) - 4)); rx.xid = cpu_to_je32(xd->xid); - if (!is_xattr_datum_dead(xd) && !(xd->flags & JFFS2_XFLAGS_INVALID)) { - rx.version = cpu_to_je32(++xd->version); - rx.xprefix = xd->xprefix; - rx.name_len = xd->name_len; - rx.value_len = cpu_to_je16(xd->value_len); - rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); - } else { - rx.version = cpu_to_je32(XDATUM_DELETE_MARKER); - } + rx.version = cpu_to_je32(++xd->version); + rx.xprefix = xd->xprefix; + rx.name_len = xd->name_len; + rx.value_len = cpu_to_je16(xd->value_len); + rx.data_crc = cpu_to_je32(crc32(0, vecs[1].iov_base, vecs[1].iov_len)); rx.node_crc = cpu_to_je32(crc32(0, &rx, sizeof(struct jffs2_raw_xattr) - 4)); - rc = jffs2_flash_writev(c, vecs, nvecs, phys_ofs, &length, 0); + rc = jffs2_flash_writev(c, vecs, 2, phys_ofs, &length, 0); if (rc || totlen != length) { JFFS2_WARNING("jffs2_flash_writev()=%d, req=%u, wrote=%zu, at %#08x\n", rc, totlen, length, phys_ofs); @@ -405,53 +394,36 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, return xd; } -static void delete_xattr_datum_delay(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) +static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ BUG_ON(xd->refcnt); unload_xattr_datum(c, xd); - set_xattr_datum_dead(xd); + xd->flags |= JFFS2_XFLAGS_DEAD; spin_lock(&c->erase_completion_lock); - list_add(&xd->xindex, &c->xattr_dead_list); - spin_unlock(&c->erase_completion_lock); - JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. " - "An orphan xdatum may be detected on next mounting.\n", xd->xid); -} - -static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) -{ - /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */ - int rc; - BUG_ON(xd->refcnt); - - unload_xattr_datum(c, xd); - set_xattr_datum_dead(xd); - rc = save_xattr_datum(c, xd); - if (rc) { - JFFS2_NOTICE("xdatum(xid=%u) was removed without delete marker. " - "An orphan xdatum may be detected on next mounting.\n", - xd->xid); + if (xd->node == (void *)xd) { + BUG_ON(!(xd->flags & JFFS2_XFLAGS_INVALID)); + jffs2_free_xattr_datum(xd); + } else { + list_add(&xd->xindex, &c->xattr_dead_list); } - spin_lock(&c->erase_completion_lock); - list_add(&xd->xindex, &c->xattr_dead_list); spin_unlock(&c->erase_completion_lock); + dbg_xattr("xdatum(xid=%u, version=%u) was removed.\n", xd->xid, xd->version); } /* -------- xref related functions ------------------ * verify_xattr_ref(c, ref) * is used to load xref information from medium. Because summary data does not * contain xid/ino, it's necessary to verify once while mounting process. - * delete_xattr_ref_node(c, ref) - * is used to delete a jffs2 node is dominated by xref. When EBS is enabled, - * it overwrites the obsolete node by myself. - * delete_xattr_ref(c, ref) - * is used to delete jffs2_xattr_ref object. If the reference counter of xdatum - * is refered by this xref become 0, delete_xattr_datum() is called later. * save_xattr_ref(c, ref) - * is used to write xref to medium. + * is used to write xref to medium. If delete marker is marked, it write + * a delete marker of xref into medium. * create_xattr_ref(c, ic, xd) * is used to create a new xref and write to medium. + * delete_xattr_ref(c, ref) + * is used to delete jffs2_xattr_ref. It marks xref XREF_DELETE_MARKER, + * and allows GC to reclaim those physical nodes. * jffs2_xattr_delete_inode(c, ic) * is called to remove xrefs related to obsolete inode when inode is unlinked. * jffs2_xattr_free_inode(c, ic) @@ -591,13 +563,13 @@ static struct jffs2_xattr_ref *create_xattr_ref(struct jffs2_sb_info *c, struct return ref; /* success */ } -static void delete_xattr_ref_delay(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) +static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref) { /* must be called under down_write(xattr_sem) */ struct jffs2_xattr_datum *xd; - set_xattr_ref_dead(ref); xd = ref->xd; + ref->xseqno |= XREF_DELETE_MARKER; ref->ino = ref->ic->ino; ref->xid = ref->xd->xid; spin_lock(&c->erase_completion_lock); @@ -605,102 +577,29 @@ static void delete_xattr_ref_delay(struct jffs2_sb_info *c, struct jffs2_xattr_r c->xref_dead_list = ref; spin_unlock(&c->erase_completion_lock); - JFFS2_NOTICE("xref(ino=%u, xid=%u) was removed without delete marker. " - "An orphan xref may be detected on next mounting.\n", - ref->ino, ref->xid); + dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", + ref->ino, ref->xid, ref->xseqno); if (!--xd->refcnt) - delete_xattr_datum_delay(c, xd); -} - -static int delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, int enforce) -{ - /* must be called under jffs2_reserve_space() and down_write(xattr_sem) */ - struct jffs2_inode_cache *ic; - struct jffs2_xattr_datum *xd; - uint32_t length; - int rc; - - set_xattr_ref_dead(ref); - ic = ref->ic; - xd = ref->xd; - ref->ino = ic->ino; - ref->xid = xd->xid; - rc = save_xattr_ref(c, ref); - if (rc) { - if (!enforce) { - clr_xattr_ref_dead(ref); - ref->ic = ic; - ref->xd = xd; - return rc; - } - JFFS2_WARNING("could not write delete marker of xref(ino=%u, xid=%u). " - "An orphan xref may be detected on next mounting.\n", - ref->ic->ino, 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); - - xd->refcnt--; - if (xd->refcnt) - return 0; - - /* delete xdatum */ - unload_xattr_datum(c, xd); - up_write(&c->xattr_sem); - jffs2_complete_reservation(c); - - rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xattr)), &length, - ALLOC_DELETION, JFFS2_SUMMARY_XATTR_SIZE); - if (rc) { - down(&c->alloc_sem); - down_write(&c->xattr_sem); - delete_xattr_datum_delay(c, xd); - } else { - down_write(&c->xattr_sem); delete_xattr_datum(c, xd); - } - return 0; } void jffs2_xattr_delete_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) { /* It's called from jffs2_clear_inode() on inode removing. When an inode with XATTR is removed, those XATTRs must be removed. */ - struct jffs2_xattr_ref *ref; - uint32_t length; - int rc, retry; + struct jffs2_xattr_ref *ref, *_ref; if (!ic || ic->nlink > 0) return; - down_read(&c->xattr_sem); - if (!ic->xref) { - up_read(&c->xattr_sem); - return; - } - up_read(&c->xattr_sem); - retry: - rc = jffs2_reserve_space(c, PAD(sizeof(struct jffs2_raw_xref)), &length, - ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE); down_write(&c->xattr_sem); - if (ic->xref) { - ref = ic->xref; - ic->xref = ref->next; - if (rc) { - delete_xattr_ref_delay(c, ref); - } else { - delete_xattr_ref(c, ref, 1); - } + for (ref = ic->xref; ref; ref = _ref) { + _ref = ref->next; + delete_xattr_ref(c, ref); } - retry = ic->xref ? 1 : 0; + ic->xref = NULL; up_write(&c->xattr_sem); - if (!rc) - jffs2_complete_reservation(c); - if (retry) - goto retry; } void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) @@ -743,7 +642,7 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac rc = load_xattr_datum(c, ref->xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref_delay(c, ref); + delete_xattr_ref(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -755,7 +654,7 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac ref->xd->flags &= ~JFFS2_XFLAGS_BIND; if (unlikely(rc > 0)) { *pcmp = cmp->next; - delete_xattr_ref_delay(c, cmp); + delete_xattr_ref(c, cmp); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -764,10 +663,10 @@ static int check_xattr_ref_inode(struct jffs2_sb_info *c, struct jffs2_inode_cac && !strcmp(ref->xd->xname, cmp->xd->xname)) { if (ref->xseqno > cmp->xseqno) { *pcmp = cmp->next; - delete_xattr_ref_delay(c, cmp); + delete_xattr_ref(c, cmp); } else { *pref = ref->next; - delete_xattr_ref_delay(c, ref); + delete_xattr_ref(c, ref); } goto retry; } @@ -865,20 +764,11 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) struct jffs2_inode_cache *ic; struct jffs2_raw_node_ref *raw; int i, xdatum_count = 0, xdatum_unchecked_count = 0, xref_count = 0; + int xdatum_orphan_count = 0, xref_orphan_count = 0, xref_dead_count = 0; BUG_ON(!(c->flags & JFFS2_SB_FLAG_BUILDING)); - /* Phase.1 : Drop dead xdatum */ - for (i=0; i < XATTRINDEX_HASHSIZE; i++) { - list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { - BUG_ON(xd->node == (void *)xd); - if (is_xattr_datum_dead(xd)) { - list_del_init(&xd->xindex); - list_add(&xd->xindex, &c->xattr_unchecked); - } - } - } - /* Phase.2 : Merge same xref */ + /* Phase.1 : Merge same xref */ for (i=0; i < XREF_TMPHASH_SIZE; i++) xref_tmphash[i] = NULL; for (ref=c->xref_temp; ref; ref=_ref) { @@ -919,13 +809,15 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) } c->xref_temp = NULL; - /* Phase.3 : Bind xref with inode_cache and xattr_datum */ + /* Phase.2 : Bind xref with inode_cache and xattr_datum */ for (i=0; i < XREF_TMPHASH_SIZE; i++) { for (ref=xref_tmphash[i]; ref; ref=_ref) { + xref_count++; _ref = ref->next; if (is_xattr_ref_dead(ref)) { ref->next = c->xref_dead_list; c->xref_dead_list = ref; + xref_dead_count++; continue; } /* At this point, ref->xid and ref->ino contain XID and inode number. @@ -933,11 +825,12 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) xd = jffs2_find_xattr_datum(c, ref->xid); ic = jffs2_get_ino_cache(c, ref->ino); if (!xd || !ic) { - JFFS2_WARNING("xref(ino=%u, xid=%u, xseqno=%u) is orphan. \n", - ref->ino, ref->xid, ref->xseqno); - set_xattr_ref_dead(ref); + dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) is orphan.\n", + ref->ino, ref->xid, ref->xseqno); + ref->xseqno |= XREF_DELETE_MARKER; ref->next = c->xref_dead_list; c->xref_dead_list = ref; + xref_orphan_count++; continue; } ref->xd = xd; @@ -945,19 +838,20 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) xd->refcnt++; ref->next = ic->xref; ic->xref = ref; - xref_count++; } } - /* Phase.4 : Link unchecked xdatum to xattr_unchecked list */ + /* Phase.3 : Link unchecked xdatum to xattr_unchecked list */ for (i=0; i < XATTRINDEX_HASHSIZE; i++) { list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { + xdatum_count++; list_del_init(&xd->xindex); if (!xd->refcnt) { - JFFS2_WARNING("orphan xdatum(xid=%u, version=%u)\n", - xd->xid, xd->version); - set_xattr_datum_dead(xd); + dbg_xattr("xdatum(xid=%u, version=%u) is orphan.\n", + xd->xid, xd->version); + xd->flags |= JFFS2_XFLAGS_DEAD; list_add(&xd->xindex, &c->xattr_unchecked); + xdatum_orphan_count++; continue; } if (is_xattr_datum_unchecked(c, xd)) { @@ -966,12 +860,14 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) list_add(&xd->xindex, &c->xattr_unchecked); xdatum_unchecked_count++; } - xdatum_count++; } } /* build complete */ - JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum (%u unchecked) and " - "%u of xref found.\n", xdatum_count, xdatum_unchecked_count, xref_count); + JFFS2_NOTICE("complete building xattr subsystem, %u of xdatum" + " (%u unchecked, %u orphan) and " + "%u of xref (%u dead, %u orphan) found.\n", + xdatum_count, xdatum_unchecked_count, xdatum_orphan_count, + xref_count, xref_dead_count, xref_orphan_count); } struct jffs2_xattr_datum *jffs2_setup_xattr_datum(struct jffs2_sb_info *c, @@ -1079,7 +975,7 @@ ssize_t jffs2_listxattr(struct dentry *dentry, char *buffer, size_t size) rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref_delay(c, ref); + delete_xattr_ref(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -1140,7 +1036,7 @@ int do_jffs2_getxattr(struct inode *inode, int xprefix, const char *xname, rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref_delay(c, ref); + delete_xattr_ref(c, ref); goto retry; } else if (unlikely(rc < 0)) { goto out; @@ -1203,7 +1099,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, rc = load_xattr_datum(c, xd); if (unlikely(rc > 0)) { *pref = ref->next; - delete_xattr_ref_delay(c, ref); + delete_xattr_ref(c, ref); goto retry; } else if (unlikely(rc < 0)) goto out; @@ -1214,8 +1110,23 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, goto out; } if (!buffer) { - *pref = ref->next; - rc = delete_xattr_ref(c, ref, 0); + ref->ino = ic->ino; + ref->xid = xd->xid; + ref->xseqno |= XREF_DELETE_MARKER; + rc = save_xattr_ref(c, ref); + if (!rc) { + *pref = ref->next; + spin_lock(&c->erase_completion_lock); + ref->next = c->xref_dead_list; + c->xref_dead_list = ref; + spin_unlock(&c->erase_completion_lock); + if (!--xd->refcnt) + delete_xattr_datum(c, xd); + } else { + ref->ic = ic; + ref->xd = xd; + ref->xseqno &= ~XREF_DELETE_MARKER; + } goto out; } goto found; @@ -1248,7 +1159,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); xd->refcnt--; if (!xd->refcnt) - delete_xattr_datum_delay(c, xd); + delete_xattr_datum(c, xd); up_write(&c->xattr_sem); return rc; } @@ -1263,21 +1174,9 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, rc = PTR_ERR(newref); xd->refcnt--; if (!xd->refcnt) - delete_xattr_datum_delay(c, xd); + delete_xattr_datum(c, xd); } else if (ref) { - up_write(&c->xattr_sem); - jffs2_complete_reservation(c); - - rc = jffs2_reserve_space(c, request, &length, - ALLOC_DELETION, JFFS2_SUMMARY_XREF_SIZE); - down_write(&c->xattr_sem); - if (rc) { - JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); - delete_xattr_ref_delay(c, ref); - up_write(&c->xattr_sem); - return 0; - } - delete_xattr_ref(c, ref, 1); + delete_xattr_ref(c, ref); } out: up_write(&c->xattr_sem); @@ -1292,6 +1191,10 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, * is used to move xref into new node. * jffs2_verify_xattr(c) * is used to call do_verify_xattr_datum() before garbage collecting. + * jffs2_release_xattr_datum(c, xd) + * is used to release an in-memory object of xdatum. + * jffs2_release_xattr_ref(c, ref) + * is used to release an in-memory object of xref. * -------------------------------------------------- */ int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd, struct jffs2_raw_node_ref *raw) @@ -1302,18 +1205,17 @@ int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xatt down_write(&c->xattr_sem); if (xd->node != raw) goto out; - if (is_xattr_datum_dead(xd) && (raw->next_in_ino == (void *)xd)) + if (xd->flags & (JFFS2_XFLAGS_DEAD|JFFS2_XFLAGS_INVALID)) goto out; - old_ofs = ref_offset(xd->node); - totlen = ref_totlen(c, c->gcblock, xd->node); - - if (!is_xattr_datum_dead(xd)) { - rc = load_xattr_datum(c, xd); - if (unlikely(rc < 0)) - goto out; + rc = load_xattr_datum(c, xd); + if (unlikely(rc)) { + rc = (rc > 0) ? 0 : rc; + goto out; } - + old_ofs = ref_offset(xd->node); + totlen = PAD(sizeof(struct jffs2_raw_xattr) + + xd->name_len + 1 + xd->value_len); rc = jffs2_reserve_space_gc(c, totlen, &length, JFFS2_SUMMARY_XATTR_SIZE); if (rc) { JFFS2_WARNING("jffs2_reserve_space_gc()=%d, request=%u\n", rc, totlen); @@ -1331,7 +1233,6 @@ int jffs2_garbage_collect_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xatt return rc; } - int jffs2_garbage_collect_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *ref, struct jffs2_raw_node_ref *raw) { @@ -1392,7 +1293,7 @@ int jffs2_verify_xattr(struct jffs2_sb_info *c) raw->flash_offset = ref_offset(raw) | ((xd->node == (void *)raw) ? REF_PRISTINE : REF_NORMAL); } - if (is_xattr_datum_dead(xd)) + if (xd->flags & JFFS2_XFLAGS_DEAD) list_add(&xd->xindex, &c->xattr_dead_list); spin_unlock(&c->erase_completion_lock); } @@ -1403,7 +1304,7 @@ int jffs2_verify_xattr(struct jffs2_sb_info *c) void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under spin_lock(&c->erase_completion_lock) */ - if (xd->node != (void *)xd) + if (xd->refcnt > 0 || xd->node != (void *)xd) return; list_del(&xd->xindex); @@ -1421,8 +1322,8 @@ void jffs2_release_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re for (tmp=c->xref_dead_list, ptmp=&c->xref_dead_list; tmp; ptmp=&tmp->next, tmp=tmp->next) { if (ref == tmp) { *ptmp = tmp->next; - jffs2_free_xattr_ref(ref); break; } } + jffs2_free_xattr_ref(ref); } diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 06ab7b88021..4a10abc6d91 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -16,6 +16,7 @@ #define JFFS2_XFLAGS_HOT (0x01) /* This datum is HOT */ #define JFFS2_XFLAGS_BIND (0x02) /* This datum is not reclaimed */ +#define JFFS2_XFLAGS_DEAD (0x40) /* This datum is already dead */ #define JFFS2_XFLAGS_INVALID (0x80) /* This datum contains crc error */ struct jffs2_xattr_datum @@ -60,34 +61,12 @@ struct jffs2_xattr_ref struct jffs2_xattr_ref *next; /* chained from ic->xref_list */ }; -#define XDATUM_DELETE_MARKER (0xffffffff) #define XREF_DELETE_MARKER (0x00000001) -static inline int is_xattr_datum_dead(struct jffs2_xattr_datum *xd) -{ - return (xd->version == XDATUM_DELETE_MARKER); -} - -static inline void set_xattr_datum_dead(struct jffs2_xattr_datum *xd) -{ - xd->version = XDATUM_DELETE_MARKER; -} - static inline int is_xattr_ref_dead(struct jffs2_xattr_ref *ref) { return ((ref->xseqno & XREF_DELETE_MARKER) != 0); } -static inline void set_xattr_ref_dead(struct jffs2_xattr_ref *ref) -{ - ref->xseqno |= XREF_DELETE_MARKER; -} - -static inline void clr_xattr_ref_dead(struct jffs2_xattr_ref *ref) -{ - ref->xseqno &= ~XREF_DELETE_MARKER; -} - - #ifdef CONFIG_JFFS2_FS_XATTR extern void jffs2_init_xattr_subsystem(struct jffs2_sb_info *c); -- cgit v1.2.3 From 355ed4e141203fd7266ef9d90d57be0c61bd1aa4 Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 24 Jun 2006 09:15:36 +0900 Subject: [JFFS2][XATTR] Fix memory leak with jffs2_xattr_ref If xattr_ref is associated with an orphan inode_cache on filesystem mounting, those xattr_refs are not released even if this inode_cache is released. This patch enables to call jffs2_xattr_delete_inode() for such a irregular inode_cachde too. Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/fs.c | 2 -- fs/jffs2/gc.c | 1 + fs/jffs2/nodelist.c | 3 +++ fs/jffs2/readinode.c | 1 + 4 files changed, 5 insertions(+), 2 deletions(-) (limited to 'fs/jffs2') diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index 2900ec3ec3a..97caa77d60c 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -227,8 +227,6 @@ void jffs2_clear_inode (struct inode *inode) struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); - - jffs2_xattr_delete_inode(c, f->inocache); jffs2_do_clear_inode(c, f); } diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index f59b147661c..daff3341ff9 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -165,6 +165,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ic->ino)); spin_unlock(&c->inocache_lock); + jffs2_xattr_delete_inode(c, ic); continue; } switch(ic->state) { diff --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 927dfe42ba7..7675b33396c 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -906,6 +906,9 @@ void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old) { struct jffs2_inode_cache **prev; +#ifdef CONFIG_JFFS2_FS_XATTR + BUG_ON(old->xref); +#endif dbg_inocache("del %p (ino #%u)\n", old, old->ino); spin_lock(&c->inocache_lock); diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 5fec012b02e..cc1899268c4 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -968,6 +968,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) struct jffs2_full_dirent *fd, *fds; int deleted; + jffs2_xattr_delete_inode(c, f->inocache); down(&f->sem); deleted = f->inocache && !f->inocache->nlink; -- cgit v1.2.3 From 2c887e2359f6e7217cdaa17994ca94ef328b658f Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 24 Jun 2006 09:16:50 +0900 Subject: [JFFS2][XATTR] Re-define xd->refcnt as atomic_t In jffs2_release_xattr_datum(), it refers xd->refcnt to ensure whether releasing xd is allowed or not. But we can't hold xattr_sem since this function is called under spin_lock(&c->erase_completion_lock). Thus we have to refer it without any locking. This patch redefine xd->refcnt as atomic_t. It enables to refer xd->refcnt without any locking. Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/xattr.c | 25 +++++++++++-------------- fs/jffs2/xattr.h | 2 +- 2 files changed, 12 insertions(+), 15 deletions(-) (limited to 'fs/jffs2') diff --git a/fs/jffs2/xattr.c b/fs/jffs2/xattr.c index 7622f79cb5b..18e66dbf23b 100644 --- a/fs/jffs2/xattr.c +++ b/fs/jffs2/xattr.c @@ -345,7 +345,7 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, && xd->value_len==xsize && !strcmp(xd->xname, xname) && !memcmp(xd->xvalue, xvalue, xsize)) { - xd->refcnt++; + atomic_inc(&xd->refcnt); return xd; } } @@ -365,7 +365,7 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, strcpy(data, xname); memcpy(data + name_len + 1, xvalue, xsize); - xd->refcnt = 1; + atomic_set(&xd->refcnt, 1); xd->xid = ++c->highest_xid; xd->flags |= JFFS2_XFLAGS_HOT; xd->xprefix = xprefix; @@ -397,7 +397,7 @@ static struct jffs2_xattr_datum *create_xattr_datum(struct jffs2_sb_info *c, static void delete_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under down_write(xattr_sem) */ - BUG_ON(xd->refcnt); + BUG_ON(atomic_read(&xd->refcnt)); unload_xattr_datum(c, xd); xd->flags |= JFFS2_XFLAGS_DEAD; @@ -580,7 +580,7 @@ static void delete_xattr_ref(struct jffs2_sb_info *c, struct jffs2_xattr_ref *re dbg_xattr("xref(ino=%u, xid=%u, xseqno=%u) was removed.\n", ref->ino, ref->xid, ref->xseqno); - if (!--xd->refcnt) + if (atomic_dec_and_test(&xd->refcnt)) delete_xattr_datum(c, xd); } @@ -612,8 +612,7 @@ void jffs2_xattr_free_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *i for (ref = ic->xref; ref; ref = _ref) { _ref = ref->next; xd = ref->xd; - xd->refcnt--; - if (!xd->refcnt) { + if (atomic_dec_and_test(&xd->refcnt)) { unload_xattr_datum(c, xd); jffs2_free_xattr_datum(xd); } @@ -835,7 +834,7 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) } ref->xd = xd; ref->ic = ic; - xd->refcnt++; + atomic_inc(&xd->refcnt); ref->next = ic->xref; ic->xref = ref; } @@ -846,7 +845,7 @@ void jffs2_build_xattr_subsystem(struct jffs2_sb_info *c) list_for_each_entry_safe(xd, _xd, &c->xattrindex[i], xindex) { xdatum_count++; list_del_init(&xd->xindex); - if (!xd->refcnt) { + if (!atomic_read(&xd->refcnt)) { dbg_xattr("xdatum(xid=%u, version=%u) is orphan.\n", xd->xid, xd->version); xd->flags |= JFFS2_XFLAGS_DEAD; @@ -1120,7 +1119,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, ref->next = c->xref_dead_list; c->xref_dead_list = ref; spin_unlock(&c->erase_completion_lock); - if (!--xd->refcnt) + if (atomic_dec_and_test(&xd->refcnt)) delete_xattr_datum(c, xd); } else { ref->ic = ic; @@ -1157,8 +1156,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, down_write(&c->xattr_sem); if (rc) { JFFS2_WARNING("jffs2_reserve_space()=%d, request=%u\n", rc, request); - xd->refcnt--; - if (!xd->refcnt) + if (atomic_dec_and_test(&xd->refcnt)) delete_xattr_datum(c, xd); up_write(&c->xattr_sem); return rc; @@ -1172,8 +1170,7 @@ int do_jffs2_setxattr(struct inode *inode, int xprefix, const char *xname, ic->xref = ref; } rc = PTR_ERR(newref); - xd->refcnt--; - if (!xd->refcnt) + if (atomic_dec_and_test(&xd->refcnt)) delete_xattr_datum(c, xd); } else if (ref) { delete_xattr_ref(c, ref); @@ -1304,7 +1301,7 @@ int jffs2_verify_xattr(struct jffs2_sb_info *c) void jffs2_release_xattr_datum(struct jffs2_sb_info *c, struct jffs2_xattr_datum *xd) { /* must be called under spin_lock(&c->erase_completion_lock) */ - if (xd->refcnt > 0 || xd->node != (void *)xd) + if (atomic_read(&xd->refcnt) || xd->node != (void *)xd) return; list_del(&xd->xindex); diff --git a/fs/jffs2/xattr.h b/fs/jffs2/xattr.h index 4a10abc6d91..06a5c69dcf8 100644 --- a/fs/jffs2/xattr.h +++ b/fs/jffs2/xattr.h @@ -28,7 +28,7 @@ struct jffs2_xattr_datum uint16_t xprefix; /* see JFFS2_XATTR_PREFIX_* */ struct list_head xindex; /* chained from c->xattrindex[n] */ - uint32_t refcnt; /* # of xattr_ref refers this */ + atomic_t refcnt; /* # of xattr_ref refers this */ uint32_t xid; uint32_t version; -- cgit v1.2.3 From 332a6b99c587161a972eaa1b00ae0e0dc49fbbfd Mon Sep 17 00:00:00 2001 From: KaiGai Kohei Date: Sat, 24 Jun 2006 09:17:42 +0900 Subject: [JFFS2][XATTR] Fix wrong copyright summary.c was modified at 2006. Signed-off-by: KaiGai Kohei Signed-off-by: David Woodhouse --- fs/jffs2/summary.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/jffs2') diff --git a/fs/jffs2/summary.c b/fs/jffs2/summary.c index c430f1d217e..1deb92acd2b 100644 --- a/fs/jffs2/summary.c +++ b/fs/jffs2/summary.c @@ -5,7 +5,7 @@ * Zoltan Sogor , * Patrik Kluba , * University of Szeged, Hungary - * 2005 KaiGai Kohei + * 2006 KaiGai Kohei * * For licensing information, see the file 'LICENCE' in this directory. * -- cgit v1.2.3