getdref(b, MKREF(cleaning));
igrab(ino);
}
- if (list_empty(&b->cleaning))
- list_add_tail(&b->cleaning, &tc->cleaning);
+ if (LAFSI(ino)->type == TypeInodeFile ||
+ LAFSI(ino)->type == TypeDir) {
+ /* Could become an orphan just now, so need
+ * to protect b->cleaning
+ */
+ spin_lock(&fs->lock);
+ list_move_tail(&b->cleaning, &tc->cleaning);
+ spin_unlock(&fs->lock);
+ } else {
+ /* No locking needed */
+ if (list_empty(&b->cleaning))
+ list_add_tail(&b->cleaning, &tc->cleaning);
+ }
/* FIXME do I need a memory barrier. to ensure truncate
* sees the not-list_empty, and we see i_size? */
* ref now
*/
done_cleaning:
+ clear_bit(B_Cleaning, &b->b.flags);
+
list_del_init(&b->cleaning);
- if (test_and_clear_bit(B_Cleaning, &b->b.flags)) {
- ino = b->b.inode;
- putdref(b, MKREF(cleaning));
- iput(ino);
+ if (test_bit(B_Orphan, &b->b.flags)) {
+ spin_lock(&fs->lock);
+ if (test_bit(B_Orphan, &b->b.flags) &&
+ list_empty(&b->orphans)) {
+ list_add(&b->orphans, &fs->pending_orphans);
+ lafs_wake_thread(fs);
+ }
+ spin_unlock(&fs->lock);
}
+
+ ino = b->b.inode;
+ putdref(b, MKREF(cleaning));
+ iput(ino);
putref(cb, MKREF(clean2));
if (rv)
goto out;
if (!list_empty_careful(&db->cleaning)) {
struct fs *fs = fs_from_inode(db->b.inode);
mutex_lock(&fs->cleaner.lock);
- if (!list_empty(&db->cleaning))
- list_del_init(&db->cleaning);
if (test_and_clear_bit(B_Cleaning, &db->b.flags)) {
+ /* This must be on the cleaner list, so
+ * it is safe to delete without a spinlock
+ */
+ list_del_init(&db->cleaning);
putdref(db, MKREF(cleaning));
iput(db->b.inode);
if (test_and_clear_bit(B_Async, &db->b.flags)) {
putdref(db, MKREF(async));
lafs_wake_thread(fs);
}
+ if (test_bit(B_Orphan, &db->b.flags)) {
+ spin_lock(&fs->lock);
+ if (test_bit(B_Orphan, &db->b.flags) &&
+ list_empty(&db->orphans)) {
+ list_add(&db->orphans, &fs->pending_orphans);
+ lafs_wake_thread(fs);
+ }
+ spin_unlock(&fs->lock);
+ }
}
mutex_unlock(&fs->cleaner.lock);
}
return err;
}
-static int lafs_drop_orphan(struct fs *fs, struct datablock *db);
+static void lafs_drop_orphan(struct fs *fs, struct datablock *db);
/*
* When any processing of an orphan makes it not an orphan any more
* (e.g. link is created for a file, directory block is cleaned)
return timeout;
}
-int lafs_drop_orphan(struct fs *fs, struct datablock *db)
+static void lafs_drop_orphan(struct fs *fs, struct datablock *db)
{
/* This block was an orphan but isn't any more.
* Remove it from the list.
orphan_iput(ino);
if (test_bit(B_Orphan, &db->b.flags))
- return 0;
+ return;
spin_lock(&fs->lock);
- if (test_bit(B_Orphan, &db->b.flags) ||
- list_empty(&db->orphans)) {
- /* Is an orphan again, or it is already removed */
- spin_unlock(&fs->lock);
- return 0;
- } else {
+ if (!test_bit(B_Orphan, &db->b.flags) &&
+ !list_empty_careful(&db->orphans) &&
+ !test_bit(B_Cleaning, &db->b.flags))
list_del_init(&db->orphans);
- spin_unlock(&fs->lock);
- return 1;
- }
+ spin_unlock(&fs->lock);
}
void lafs_add_orphan(struct fs *fs, struct datablock *db)
*/
LAFS_BUG(!test_bit(B_Orphan, &db->b.flags), &db->b);
spin_lock(&fs->lock);
- if (list_empty(&db->orphans))
+ if (list_empty_careful(&db->orphans))
list_add_tail(&db->orphans, &fs->pending_orphans);
-
spin_unlock(&fs->lock);
lafs_wake_thread(fs);
}
* it just now. When we do, lafs_add_orphan will be called */
LAFS_BUG(!test_bit(B_Orphan, &db->b.flags), &db->b);
spin_lock(&fs->lock);
- if (!list_empty(&db->orphans))
+ if (!test_bit(B_Cleaning, &db->b.flags) &&
+ !list_empty_careful(&db->orphans))
list_del_init(&db->orphans);
spin_unlock(&fs->lock);
}
u32 orphan_slot; /* slot in orphan file to record that this
* block is an orphan
*/
- struct list_head orphans; /* linked list of blocks needing orphan
- * processing.
- */
- struct list_head cleaning; /* list of blocks being cleaned.
- * Could share with orphans FIXME
+ union {
+ /* If a block is both an orphan and undergoing
+ * cleaning, it lives on the cleaning list until
+ * the cleaner has checked it. It is then moved
+ * to the pending_orphans list.
+ */
+ struct list_head orphans; /* linked list of blocks needing orphan
+ * processing.
*/
+ struct list_head cleaning; /* list of blocks being cleaned.
+ */
+ };
union {
struct inode *my_inode; /* only valid for block holding an inode */
};