Subject: afs/fs/cachefs super.c,1.40,1.41 kcachefs_jnld.c,1.7,1.8 journal.c,1.45,1.46 interface.c,1.17,1.18 index.c,1.31,1.32 cachefs-int.h,1.43,1.44 block.c,1.11,1.12

From: [email-masked]
Date: Tue, 16 Sep 2003 17:49:28 +0100

Update of /home/cvs/afs/fs/cachefs
In directory phoenix.infradead.org:/tmp/cvs-serv19856/fs/cachefs

Modified Files:
	super.c kcachefs_jnld.c journal.c interface.c index.c 
	cachefs-int.h block.c 
Log Message:
added unjournalled block alteration and cookie index data update capability

Index: super.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/super.c,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -r1.40 -r1.41
--- super.c	15 Sep 2003 18:00:19 -0000	1.40
+++ super.c	16 Sep 2003 16:49:25 -0000	1.41
@@ -293,10 +293,9 @@
 	INIT_LIST_HEAD(&super->ujnl_markq);
 	INIT_LIST_HEAD(&super->ujnl_commitq);
 	INIT_LIST_HEAD(&super->ujnl_writeq);
-	INIT_LIST_HEAD(&super->ujnl_ackq);
-	INIT_LIST_HEAD(&super->ujnl_ackwq);
 	INIT_LIST_HEAD(&super->ujnl_replayq);
 
+	spin_lock_init(&super->njalt_lock);
 	INIT_LIST_HEAD(&super->jnld_link);
 
 	init_MUTEX(&super->batch_sem);

Index: kcachefs_jnld.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/kcachefs_jnld.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- kcachefs_jnld.c	3 Sep 2003 12:44:19 -0000	1.7
+++ kcachefs_jnld.c	16 Sep 2003 16:49:25 -0000	1.8
@@ -115,11 +115,7 @@
 		down(&kcachefs_jnld_list_sem);
 
 #if 0
-		list_for_each(_p, &kcachefs_jnld_list) {
-			super = list_entry(_p,
-					   struct cachefs_super,
-					   jnld_link);
-
+		list_for_each_entry(super, &kcachefs_jnld_list, jnld_link) {
 			cachefs_trans_write_batch(super);
 
 			if (super->ujnl_do_acks)

Index: journal.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/journal.c,v
retrieving revision 1.45
retrieving revision 1.46
diff -u -r1.45 -r1.46
--- journal.c	15 Sep 2003 18:00:19 -0000	1.45
+++ journal.c	16 Sep 2003 16:49:25 -0000	1.46
@@ -301,30 +301,9 @@
 
 		BUG_ON(!effect->block->page);
 
-#ifndef CACHEFS_BLOCK_USE_COW
-		/* wait for the page to be written back */
-		if (test_bit(CACHEFS_BLOCK_WRITEBACK, &effect->block->flags)) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			add_wait_queue(&effect->block->writewq, &myself);
-
-			while (test_bit(CACHEFS_BLOCK_WRITEBACK,
-					&effect->block->flags)) {
-				if (signal_pending(current))
-					break;
-
-				schedule();
-				set_current_state(TASK_INTERRUPTIBLE);
-			}
-
-			set_current_state(TASK_RUNNING);
-			remove_wait_queue(&effect->block->writewq, &myself);
-		}
-
-#else
-		/* duplicate the page */
+		/* duplicate the page if being written out */
 		if (test_bit(CACHEFS_BLOCK_COW, &effect->block->flags))
 			cachefs_block_cow(super, effect->block);
-#endif
 
 		get_page(effect->block->page);
 		effect->held_page = effect->block->page;
@@ -536,7 +515,7 @@
 
 /*****************************************************************************/
 /*
- * commit a replay transaction 
+ * commit a replay transaction
  * - discard any transaction that is "ineffective"
  */
 void cachefs_trans_commit_replay(struct cachefs_transaction *trans)
@@ -565,6 +544,126 @@
 
 /*****************************************************************************/
 /*
+ * begin an unjournalled alteration of a block
+ */
+int cachefs_block_begin_alter(struct cachefs_block *block)
+{
+	struct cachefs_alteration *alter;
+	struct cachefs_super *super = block->super;
+
+	_enter("{%u}", block->bix);
+
+	BUG_ON(!block->page);
+	BUG_ON(!block->page->private);
+	BUG_ON(test_bit(CACHEFS_BLOCK_NETFSDATA, &block->flags));
+
+	alter = kmalloc(sizeof(*alter), GFP_KERNEL);
+	if (!alter) {
+		_leave(" = -ENOMEM");
+		return -ENOMEM;
+	}
+
+	down_read(&super->batch_ctrl_sem);
+
+	alter->effect.block	= block;
+	alter->effect.held_page	= block->page;
+
+	if (!test_and_set_bit(CACHEFS_BLOCK_ALTERED, &block->flags)) {
+		cachefs_block_get(block);
+		get_page(alter->effect.held_page);
+
+		spin_lock(&super->njalt_lock);
+		alter->next = super->njalt_markq;
+		super->njalt_markq = alter;
+		spin_unlock(&super->njalt_lock);
+
+		SetPageWriteback(alter->effect.held_page);
+	}
+	else {
+		kfree(alter);
+	}
+
+	/* duplicate the page if it's currently being written out */
+	if (test_bit(CACHEFS_BLOCK_COW, &block->flags))
+		cachefs_block_cow(super, block);
+
+	_leave("");
+	return 0;
+} /* end cachefs_block_begin_alter() */
+
+/*****************************************************************************/
+/*
+ * end an unjournalled alteration of a block
+ */
+void cachefs_block_end_alter(struct cachefs_block *block)
+{
+	struct cachefs_super *super = block->super;
+
+	_enter("{%u}", block->bix);
+
+	up_read(&super->batch_ctrl_sem);
+
+	_leave("");
+} /* end cachefs_block_end_alter() */
+
+/*****************************************************************************/
+/*
+ * prepare a block for writing
+ */
+static inline void cachefs_trans_batch_prep_block(struct cachefs_super *super,
+						  struct cachefs_trans_effect *effect)
+{
+	struct cachefs_block *block = effect->block;
+	struct list_head *plist;
+
+	BUG_ON(!block->page);
+	BUG_ON(!block->page->private);
+
+	if (test_bit(CACHEFS_BLOCK_WRITEBACK, &block->flags))
+		return;
+
+	set_bit(CACHEFS_BLOCK_COW	, &block->flags);
+	set_bit(CACHEFS_BLOCK_WRITEBACK	, &block->flags);
+
+	plist = &super->batch_writeq;
+	if (test_bit(CACHEFS_BLOCK_NETFSDATA, &block->flags)) {
+		plist = &super->batch_doneq;
+		_debug("skip meta block %u", block->bix);
+	}
+
+	list_add_tail(&block->batch_link, plist);
+	block->writeback = block->page;
+	get_page(block->writeback);
+	flush_dcache_page(block->writeback);
+
+} /* end cachefs_trans_batch_prep_block() */
+
+/*****************************************************************************/
+/*
+ * prepare the unjournalled block alterations for writing
+ */
+static void cachefs_trans_batch_prep_njalt(struct cachefs_super *super)
+{
+	struct cachefs_alteration *alter;
+
+	_enter("");
+
+	BUG_ON(super->njalt_writeq);
+
+	alter = super->njalt_markq;
+	super->njalt_markq = NULL;
+	super->njalt_writeq = alter;
+
+	for (; alter; alter = alter->next) {
+		_debug("- %x", alter->effect.block->bix);
+		cachefs_trans_batch_prep_block(super, &alter->effect);
+	}
+
+	_leave("");
+} /* end cachefs_trans_batch_prep_njalt() */
+
+/*****************************************************************************/
+/*
  * prepare a transaction for writing
  * - mark all modified journalling and data blocks for writeback
  * - mark data for COW
@@ -592,32 +691,11 @@
 	}
 
 	/* mark data blocks */
-	for (loop = 0; loop < CACHEFS_EFFECTS_PER_TRANS; loop++) {
-		block = trans->effects[loop].block;
-		if (!block)
-			continue;
-
-		BUG_ON(!block->page);
-		BUG_ON(!block->page->private);
-
-		if (!test_bit(CACHEFS_BLOCK_WRITEBACK, &block->flags)) {
-			struct list_head *plist;
-
-			set_bit(CACHEFS_BLOCK_COW	,&block->flags);
-			set_bit(CACHEFS_BLOCK_WRITEBACK	,&block->flags);
-
-			plist = &super->batch_writeq;
-			if (test_bit(CACHEFS_BLOCK_NETFSDATA, &block->flags)) {
-				plist = &super->batch_doneq;
-				_debug("skip meta block %u", block->bix);
-			}
+	for (loop = 0; loop < CACHEFS_EFFECTS_PER_TRANS; loop++)
+		if (trans->effects[loop].block)
+			cachefs_trans_batch_prep_block(super,
+						       &trans->effects[loop]);
 
-			list_add_tail(&block->batch_link, plist);
-			block->writeback = block->page;
-			get_page(block->writeback);
-			flush_dcache_page(block->writeback);
-		}
-	}
 } /* end cachefs_trans_batch_write_prep_trans() */
 
 /*****************************************************************************/
@@ -628,6 +706,7 @@
 {
 	struct cachefs_ondisc_update_journal *ajentry;
 	struct cachefs_transaction *trans;
+	struct cachefs_alteration *alter;
 	struct cachefs_block *block;
 	unsigned short jstop;
 	unsigned long flags;
@@ -653,7 +732,7 @@
 	 * out */
 	down_write(&super->batch_ctrl_sem);
 
-	if (list_empty(&super->ujnl_commitq))
+	if (list_empty(&super->ujnl_commitq) && !super->njalt_markq)
 		goto nothing_to_do;
 
 	BUG_ON(!list_empty(&super->ujnl_markq));
@@ -662,6 +741,8 @@
 		cachefs_trans_batch_write_prep_trans(super, trans);
 	}
 
+	cachefs_trans_batch_prep_njalt(super);
+
 	spin_lock_irqsave(&super->ujnl_mk_lock, flags);
 	list_splice_init(&super->ujnl_commitq, &super->ujnl_writeq);
 	spin_unlock_irqrestore(&super->ujnl_mk_lock, flags);
@@ -698,45 +779,61 @@
 
 	up_write(&super->batch_ctrl_sem);
 
-	/* write to the update journal with a barrier if we're not replaying
-	 * and then write the data (if we're replaying, all the journal entries
-	 * bar the ACK already reside on disc) */
-	if (!test_bit(CACHEFS_SUPER_REPLAYING_UJNL, &super->flags))
+	/* write to the update journal with a barrier if there are any
+	 * transactions to record and if we're not replaying them (if we're
+	 * replaying, all the journal entries bar the ACK already reside on
+	 * disc) */
+	if (!test_bit(CACHEFS_SUPER_REPLAYING_UJNL, &super->flags) &&
+	    !list_empty(&super->ujnl_writeq)
+	    ) {
 		cachefs_trans_batch_write_ujnl(super, jstop);
 
-	ajentry->mark = CACHEFS_ONDISC_UJNL_BATCH;
+		ajentry->mark = CACHEFS_ONDISC_UJNL_BATCH;
 
-	if (!test_bit(CACHEFS_SUPER_REPLAYING_UJNL, &super->flags))
 		cachefs_trans_batch_write_marker(super, jstop, ajentry);
-	else
+	}
+	else {
 		up(&super->batch_uj_sem);
+	}
 
 	cachefs_trans_batch_write_data(super);
 	cachefs_trans_batch_process_written_blocks(super, 2);
 
-	/* polish off with an ACK */
-	if (!test_bit(CACHEFS_SUPER_REPLAYING_UJNL, &super->flags))
-		jstop = UJNL_WRAP(jstop + 1);
-
-	ajentry->mark = CACHEFS_ONDISC_UJNL_ACK;
-	ajentry->serial++;
-
-	cachefs_trans_batch_write_ack(super, jstop, ajentry);
-	kfree(ajentry);
-
-	super->ujnl_tail = UJNL_WRAP(jstop + 1);
-
-	/* clean up the transactions that we've now written */
-	while (!list_empty(&super->ujnl_writeq)) {
-		trans = list_entry(super->ujnl_writeq.next,
-				   struct cachefs_transaction, sblink);
-		list_del_init(&trans->sblink);
+	/* polish off with an ACK if any entries were recorded */
+	if (!list_empty(&super->ujnl_writeq)) {
+		if (!test_bit(CACHEFS_SUPER_REPLAYING_UJNL, &super->flags))
+			jstop = UJNL_WRAP(jstop + 1);
+
+		ajentry->mark = CACHEFS_ONDISC_UJNL_ACK;
+		ajentry->serial++;
+
+		cachefs_trans_batch_write_ack(super, jstop, ajentry);
+		kfree(ajentry);
+
+		super->ujnl_tail = UJNL_WRAP(jstop + 1);
+
+		/* clean up the transactions that we've now written */
+		while (!list_empty(&super->ujnl_writeq)) {
+			trans = list_entry(super->ujnl_writeq.next,
+					   struct cachefs_transaction, sblink);
+			list_del_init(&trans->sblink);
+
+			cachefs_ujnl_set_phase(trans,
+					       CACHEFS_TRANS_DEAD,
+					       CACHEFS_TRANS_COMMITTING);
 
-		cachefs_ujnl_set_phase(trans,
-				       CACHEFS_TRANS_DEAD,
-				       CACHEFS_TRANS_COMMITTING);
+			cachefs_trans_put(trans);
+		}
+	}
 
-		cachefs_trans_put(trans);
+	/* clean up any unjournalled alterations we may have written */
+	while (super->njalt_writeq) {
+		alter = super->njalt_writeq;
+		super->njalt_writeq = alter->next;
+
+		cachefs_block_put(alter->effect.block);
+		cachefs_put_page(alter->effect.held_page);
+		kfree(alter);
 	}
 
  done:
@@ -1105,8 +1202,8 @@
 
  try_again:
 	ret = cachefs_block_read(super, NULL, bix, 1, &jblock, &jpage);
-	if (ret<0)
-		goto cant_read_block;;
+	if (ret < 0)
+		goto cant_read_block;
 
 	set_bit(CACHEFS_BLOCK_UJOURNAL, &jblock->flags);
 	set_bit(CACHEFS_BLOCK_WRITEBACK, &jblock->flags);
@@ -1139,10 +1236,6 @@
 
 	//dump_bio(bio,1);
 
-#if 0
-	BUG(); /* TODO: remove (for replay testing) */
-#endif
-
 	submit_bio(WRITE | (1 << BIO_RW_BARRIER), bio);
 
 	/* wait for I/O completion */
@@ -1276,11 +1369,12 @@
 		_debug(" wrote block %05u fl=%04lx pg=%p wb=%p",
 		       block->bix, block->flags, block->page, block->writeback);
 
+		wbpage = xchg(&block->writeback, NULL);
+
 		clear_bit(CACHEFS_BLOCK_COW, &block->flags);
 		clear_bit(CACHEFS_BLOCK_WRITEBACK, &block->flags);
 		wake_up_all(&block->writewq);
 
-		wbpage = xchg(&block->writeback, NULL);
 		BUG_ON(!wbpage);
 		end_page_writeback(wbpage);
 		put_page(wbpage);

Index: interface.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/interface.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- interface.c	19 Aug 2003 13:37:07 -0000	1.17
+++ interface.c	16 Sep 2003 16:49:26 -0000	1.18
@@ -808,6 +808,37 @@
 
 /*****************************************************************************/
 /*
+ * update the index entries backing a cookie
+ */
+void __cachefs_update_cookie(struct cachefs_cookie *cookie)
+{
+	struct cachefs_inode *inode;
+
+	_enter("{%s}",
+	       cookie &&
+	       cookie->idef ? (char *) cookie->idef->name : "<file>");
+
+	if (!cookie) {
+		_leave(" [no cookie]");
+		return;
+	}
+
+	down_read(&cookie->sem);
+	down_read(&cookie->iparent->sem);
+
+	list_for_each_entry(inode, &cookie->backing_inodes, cookie_link) {
+		cachefs_index_update(inode);
+	}
+
+	up_read(&cookie->iparent->sem);
+	up_read(&cookie->sem);
+	_leave("");
+} /* end __cachefs_update_cookie() */
+
+EXPORT_SYMBOL(__cachefs_update_cookie);
+
+/*****************************************************************************/
+/*
  * see if the netfs definition matches
  */
 static cachefs_match_val_t cachefs_fsdef_index_match(void *target,

Index: index.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/index.c,v
retrieving revision 1.31
retrieving revision 1.32
diff -u -r1.31 -r1.32
--- index.c	12 Sep 2003 13:01:17 -0000	1.31
+++ index.c	16 Sep 2003 16:49:26 -0000	1.32
@@ -686,6 +686,79 @@
 
 /*****************************************************************************/
 /*
+ * update the index entry for an index or data file from the associated netfs
+ * data
+ */
+int cachefs_index_update(struct cachefs_inode *inode)
+{
+	struct cachefs_ondisc_index_entry *xent;
+	struct cachefs_ondisc_metadata *meta;
+	struct cachefs_cookie *cookie = inode->cookie;
+	struct cachefs_super *super;
+	struct cachefs_inode *index;
+	struct cachefs_block *block;
+	struct page *ixpage;
+	unsigned offs;
+	int ret;
+
+	_enter("");
+
+	super = inode->vfs_inode.i_sb->s_fs_info;
+
+	if (test_bit(CACHEFS_SUPER_WITHDRAWN, &super->flags))
+		return 0;
+
+	/* find the parent index inode */
+	list_for_each_entry(index,
+			    &cookie->iparent->backing_inodes,
+			    cookie_link) {
+		if (index->vfs_inode.i_sb == inode->vfs_inode.i_sb)
+			goto found;
+	}
+
+	/* hmmm... parent inode is strangely absent */
+	BUG();
+	return -ENOENT;
+
+ found:
+	meta = cachefs_metadata_preread(inode);
+	offs = meta->pindex_entry;
+	cachefs_metadata_postread(inode);
+
+	/* get the page holding the index data */
+	ret = cachefs_get_page(index, offs / index->index_epp, &ixpage);
+	if (ret < 0) {
+		_leave(" = %d",ret);
+		return ret;
+	}
+
+	offs = (offs % index->index_epp) * index->index_esize;
+
+	_debug("update ino=%lx pg={%lu}+%x",
+	       index->vfs_inode.i_ino, ixpage->index, offs);
+
+	block = __cachefs_get_page_block(ixpage);
+	ret = cachefs_block_begin_alter(block);
+	if (ret < 0)
+		goto error_page;
+
+	cachefs_block_modify(super, block, &ixpage);
+
+	xent = kmap(ixpage) + offs;
+	cookie->iparent->idef->update(cookie->netfs_data, xent->data);
+	kunmap(ixpage);
+
+	cachefs_block_end_alter(block);
+
+ error_page:
+	cachefs_put_page(ixpage);
+	_leave(" = %d", ret);
+	return ret;
+
+} /* end cachefs_index_update() */
+
+/*****************************************************************************/
+/*
  * mark the next inode pinned by an entry in the index currently being
  * reclaimed as being obsolete
  */

Index: cachefs-int.h
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/cachefs-int.h,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -r1.43 -r1.44
--- cachefs-int.h	15 Sep 2003 13:53:25 -0000	1.43
+++ cachefs-int.h	16 Sep 2003 16:49:26 -0000	1.44
@@ -142,10 +142,12 @@
 	struct list_head		ujnl_markq;	/* marked transactions */
 	struct list_head		ujnl_commitq;	/* committed transactions */
 	struct list_head		ujnl_writeq;	/* transactions being written */
-	struct list_head		ujnl_ackq;	/* marks ACK'd by transaction completion */
-	struct list_head		ujnl_ackwq;	/* marks awaiting on-disc ACK */
 	struct list_head		ujnl_replayq;	/* blocks having allocation replayed */
 
+	struct cachefs_alteration	*njalt_markq;	/* unjournalled alterations - marked */
+	struct cachefs_alteration	*njalt_writeq;	/* unjournalled alterations - writing */
+	spinlock_t			njalt_lock;
+
 	struct semaphore		batch_sem;	/* batching mutex */
 	struct semaphore		batch_uj_sem;	/* ujnl written sync mutex */
 	struct rw_semaphore		batch_ctrl_sem;	/* marking/batching interleave control */
@@ -227,6 +229,7 @@
 #define CACHEFS_BLOCK_WITHDRAWN	7	/* [bit] backing cache withdrawn from service */
 #define CACHEFS_BLOCK_NETFSDATA	8	/* [bit] netfs data block (discard metadata) */
 #define CACHEFS_BLOCK_NETFSBUSY	9	/* [bit] netfs is accessing the block */
+#define CACHEFS_BLOCK_ALTERED	10	/* [bit] unjournalled alteration made */
 
 #define _CACHEFS_BLOCK_ALLOC		(1 << CACHEFS_BLOCK_ALLOC)
 #define _CACHEFS_BLOCK_COW		(1 << CACHEFS_BLOCK_COW)
@@ -279,6 +282,9 @@
 extern int cachefs_block_cow(struct cachefs_super *super,
 			     struct cachefs_block *block);
 
+extern int cachefs_block_begin_alter(struct cachefs_block *block);
+extern void cachefs_block_end_alter(struct cachefs_block *block);
+
 static inline
 struct cachefs_block *cachefs_block_get(struct cachefs_block *block)
 {
@@ -451,6 +457,8 @@
 			     struct cachefs_cookie *cookie,
 			     unsigned *_newino);
 
+extern int cachefs_index_update(struct cachefs_inode *index);
+
 extern int cachefs_index_reclaim_one_entry(struct cachefs_super *super,
 					   struct cachefs_transaction **_trans);
 
@@ -484,7 +492,7 @@
 
 /*****************************************************************************/
 /*
- * transaction record and tracking structure
+ * transaction record and tracking structures
  * - these record the modification of metadata (and not, generally, ordinary data)
  */
 enum cachefs_trans_phase {
@@ -532,6 +540,13 @@
 	/* tracking for blocks being modified by this transaction */
 	unsigned			eff_active;
 	struct cachefs_trans_effect	effects[CACHEFS_EFFECTS_PER_TRANS];
+};
+
+/* record of unjournalled alteration */
+struct cachefs_alteration
+{
+	struct cachefs_alteration	*next;
+	struct cachefs_trans_effect	effect;
 };
 
 extern

Index: block.c
===================================================================
RCS file: /home/cvs/afs/fs/cachefs/block.c,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- block.c	15 Sep 2003 13:53:25 -0000	1.11
+++ block.c	16 Sep 2003 16:49:26 -0000	1.12
@@ -222,8 +222,8 @@
 	/* get a page for it if it doesn't already exist */
 	if (!block->page) {
 		/* if the block is marked as currently undergoing writeback
-		 * then there was an ENOMEM encountered whilst trying to COW
-		 * the block */
+		 * then there must have been an ENOMEM encountered whilst
+		 * trying to COW the block */
 		if (test_bit(CACHEFS_BLOCK_WRITEBACK,&block->flags)) {
 			set_current_state(TASK_INTERRUPTIBLE);
 			add_wait_queue(&block->writewq,&myself);
@@ -302,17 +302,44 @@
 
 /*****************************************************************************/
 /*
- * copy a block upon attempting to modify it and finding that it's busy being written
+ * copy a block upon attempting to modify it and finding that it's busy being
+ * written out
  */
 int cachefs_block_cow(struct cachefs_super *super, struct cachefs_block *block)
 {
-//	struct address_space *mapping;
-	struct page *page; //, *newpage;
-	filler_t filler;
-//	int ret;
+	_enter(",{%u}", block->bix);
 
 	DECLARE_WAITQUEUE(myself, current);
 
+#ifndef CACHEFS_BLOCK_USE_COW
+
+	/* if COW is not permitted, then simply wait for the page to finish
+	 * being written back */
+	if (test_bit(CACHEFS_BLOCK_COW, &block->flags)) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		add_wait_queue(&block->writewq, &myself);
+
+		while (test_bit(CACHEFS_BLOCK_COW, &block->flags)) {
+			if (signal_pending(current))
+				break;
+
+			schedule();
+			set_current_state(TASK_INTERRUPTIBLE);
+		}
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&block->writewq, &myself);
+	}
+
+	_leave(" = 0");
+	return 0;
+
+#else
+	struct address_space *mapping;
+	struct page *page, *newpage;
+	filler_t filler;
+	int ret;
+
 	_enter(",%u", block->bix);
 
 	/* get the page alloc lock for this block */
@@ -331,19 +358,18 @@
 
 	/* duplicate the page if it's flagged copy-on-write */
 	if (test_bit(CACHEFS_BLOCK_COW, &block->flags)) {
-		BUG();
-#if 0
 		struct cachefs_page *newpageio;
 
 		mapping = super->imisc->i_mapping;
 
+		ret = -ENOMEM;
 		newpage = page_cache_alloc_cold(mapping);
 		if (!newpage)
-			goto nomem;
+			goto error;
 
-		if (cachefs_get_page_private(newpage, &newpageio,
-					     mapping->gfp_mask) < 0)
-			goto nomem_page;
+		if (cachefs_page_get_private(newpage, &newpageio,
+					     mapping_gfp_mask(mapping)) < 0)
+			goto error_page;
 
 		newpageio->mapped_block =
 			cachefs_block_get(
@@ -362,15 +388,13 @@
 		page = NULL;
 
 		ret = add_to_page_cache_lru(newpage, mapping, block->bix,
-					    GFP_KERNEL);
-		if (ret<0) {
+					    mapping_gfp_mask(mapping));
+		if (ret < 0) {
 			BUG_ON(ret == -EEXIST);
-			page_cache_release(newpage);
-			return ret;
+			goto error_page;
 		}
 
 		block->page = newpage;
-#endif
 	}
 	else {
 		page = block->page;
@@ -383,15 +407,14 @@
 	_leave(" = 0");
 	return 0;
 
-#if 0
- nomem_page:
+ error_page:
 	page_cache_release(newpage);
- nomem:
+ error:
 	clear_bit(CACHEFS_BLOCK_ALLOC, &block->flags);
 	wake_up_all(&block->writewq);
 
-	_leave(" = -ENOMEM");
-	return -ENOMEM;
+	_leave(" = %d", ret);
+	return ret;
 #endif
 } /* end cachefs_block_cow() */