write barriers for 2.4.x
Subsystems (3)
PATCH 1/1 write barriers for 2.4.x
Hello everyone,
This is a modified version of Jens Axboe's write barrier patch, against 2.4.18-pre9. The idea is to allow journaled filesystems to trigger write cache flushes on ide drives, and make use of ordered tags on scsi systems (only aic7xxx is coded for scsi right now) instead of forced ordering by waiting on buffers all the time.
reiserfs support is included, mount with -o barrier to trigger the new code.
IDE performance is mostly unchanged, but you can safely enable writeback caching, which is a big gain in many usage patterns. If your IDE performance goes down with this patch, it's probably because your drive has writeback caching on by default.
My scsi performance goes down 5% (postmark, dbench, iozone, all see the same slow down) with -o barrier, which is surprising because reiserfs skips many wait_on_buffer calls when barrier mode is on. Hopefully people with aic7xxx cards can give this a try and let me know if any drives do see performance gains.
If you really want to experiment with this on scsi, but have a different adapter, let me know.
Anyway, patch is below. It should be considered highly experimental. I'm looking for benchmarkers and crash testers to try it on non-critical data.
-chris
Index: linus.22/fs/reiserfs/super.c
fs/reiserfs/super.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/41_super.c 1.2.2.1.2.1.1.1.1.1 644) renamed from fs/reiserfs/super.c Fri, 11 Jan 2002 10:14:59 -0500 root (linux/41_super.c 1.2.2.1.2.1.1.1 644) +6 -0
@@ -335,6 +335,9 @@ reiserfs_prepare_for_journal(s, SB_BUFFER_WITH_SB(s), 1) ; set_sb_state( SB_DISK_SUPER_BLOCK(s), s->u.reiserfs_sb.s_mount_state ); journal_mark_dirty(&th, s, SB_BUFFER_WITH_SB (s)); + + /* force full waits when unmounting */ + s->u.reiserfs_sb.s_mount_opt &= ~(1 << REISERFS_BARRIER) ; } /* note, journal_release checks for readonly mount, and can decide not
@@ -437,6 +440,9 @@ set_bit (REISERFS_NO_UNHASHED_RELOCATION, mount_options); } else if (!strcmp (this_char, "hashed_relocation")) { set_bit (REISERFS_HASHED_RELOCATION, mount_options); + } else if (!strcmp (this_char, "barrier")) { + set_bit (REISERFS_BARRIER, mount_options); + printk("reiserfs: enabling write barrier code\n") ; } else if (!strcmp (this_char, "test4")) { set_bit (REISERFS_TEST4, mount_options); } else if (!strcmp (this_char, "nolog")) {
Index: linus.22/fs/reiserfs/journal.c
fs/reiserfs/journal.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/b/0_journal.c 1.2.1.1.4.2.1.3 644) renamed from fs/reiserfs/journal.c Mon, 28 Jan 2002 09:51:50 -0500 root (linux/b/0_journal.c 1.2.1.1.4.2.1.2 644) +38 -6
84 lines not on this page · raw message
Index: linus.22/drivers/ide/ide-probe.c
drivers/ide/ide-probe.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/m/b/37_ide-probe. 1.3 644) renamed from drivers/ide/ide-probe.c Tue, 27 Nov 2001 23:34:21 -0500 root (linux/m/b/37_ide-probe. 1.2 644) +5 -0
@@ -597,6 +597,11 @@ q->queuedata = HWGROUP(drive); blk_init_queue(q, do_ide_request); + /* + * assume all drives can do ordered flush, disable this + * later if it fails + */ + blk_queue_ordered(q, QUEUE_ORDERED_FLUSH); } /*
Index: linus.22/drivers/ide/ide.c
drivers/ide/ide.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/n/b/8_ide.c 1.4 644) renamed from drivers/ide/ide.c Fri, 11 Jan 2002 10:17:27 -0500 root (linux/n/b/8_ide.c 1.3 644) +79 -2
113 lines not on this page · raw message
Index: linus.22/drivers/block/elevator.c
drivers/block/elevator.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/v/b/9_elevator.c 1.1.1.1 644) renamed from drivers/block/elevator.c Tue, 06 Nov 2001 15:47:39 -0500 root (linux/v/b/9_elevator.c 1.1 644) +12 -0
@@ -87,6 +87,12 @@ struct request *__rq = blkdev_entry_to_request(entry); /* + * we can neither merge nor insert before/with a flush + */ + if (__rq->cmd_flags & RQ_WRITE_ORDERED) + break; + + /* * simply "aging" of requests in queue */ if (__rq->elevator_sequence-- <= 0)
@@ -155,6 +161,12 @@ entry = &q->queue_head; while ((entry = entry->prev) != head) { struct request *__rq = blkdev_entry_to_request(entry); + + /* + * we can neither merge nor insert before/with a flush + */ + if (__rq->cmd_flags & RQ_WRITE_ORDERED) + break; if (__rq->cmd != rw) continue;
Index: linus.22/drivers/block/ll_rw_blk.c
drivers/block/ll_rw_blk.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/w/b/1_ll_rw_blk. 1.4 644) renamed from drivers/block/ll_rw_blk.c Fri, 11 Jan 2002 10:17:27 -0500 root (linux/w/b/1_ll_rw_blk. 1.3 644) +42 -2
85 lines not on this page · raw message
Index: linus.22/drivers/scsi/hosts.c
drivers/scsi/hosts.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/x/b/30_hosts.c 1.3 644) renamed from drivers/scsi/hosts.c Fri, 11 Jan 2002 10:17:27 -0500 root (linux/x/b/30_hosts.c 1.2 644) +1 -0
@@ -238,6 +238,7 @@ retval->select_queue_depths = tpnt->select_queue_depths; retval->max_sectors = tpnt->max_sectors; + retval->can_order = tpnt->can_order; if(!scsi_hostlist) scsi_hostlist = retval;
Index: linus.22/drivers/scsi/hosts.h
drivers/scsi/hosts.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/y/b/49_hosts.h 1.3 644) renamed from drivers/scsi/hosts.h Fri, 11 Jan 2002 10:17:27 -0500 root (linux/y/b/49_hosts.h 1.2 644) +7 -0
@@ -292,6 +292,11 @@ unsigned emulated:1; /* + * Driver supported ordered tags + */ + unsigned can_order:1; + + /* * Name of proc directory */ char *proc_name;
@@ -417,6 +422,8 @@ * when the device becomes less busy that we need to feed them. */ unsigned some_device_starved:1; + + unsigned int can_order:1; void (*select_queue_depths)(struct Scsi_Host *, Scsi_Device *);
Index: linus.22/drivers/scsi/scsi.c
drivers/scsi/scsi.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/A/b/7_scsi.c 1.5 644) renamed from drivers/scsi/scsi.c Fri, 11 Jan 2002 10:17:27 -0500 root (linux/A/b/7_scsi.c 1.4 644) +2 -0
@@ -188,6 +188,8 @@ */ void scsi_initialize_queue(Scsi_Device * SDpnt, struct Scsi_Host * SHpnt) { blk_init_queue(&SDpnt->request_queue, scsi_request_fn); + if (SHpnt->can_order) + blk_queue_ordered(&SDpnt->request_queue, QUEUE_ORDERED_TAG); blk_queue_headactive(&SDpnt->request_queue, 0); SDpnt->request_queue.queuedata = (void *) SDpnt; }
Index: linus.22/drivers/scsi/aic7xxx/aic7xxx_linux.c
drivers/scsi/aic7xxx/aic7xxx_linux.c Wed, 13 Feb 2002 10:47:50 -0500 root (linux/A/b/42_aic7xxx_li 1.5 644) renamed from drivers/scsi/aic7xxx/aic7xxx_linux.c Fri, 11 Jan 2002 10:17:27 -0500 root (linux/A/b/42_aic7xxx_li 1.4 644) +3 -0
@@ -1638,6 +1638,9 @@ } } + if (cmd->request.cmd_flags & RQ_WRITE_ORDERED) { + hscb->control |= MSG_ORDERED_Q_TAG; + } hscb->cdb_len = cmd->cmd_len; if (hscb->cdb_len <= 12) { memcpy(hscb->shared_data.cdb, cmd->cmnd, hscb->cdb_len);
Index: linus.22/drivers/scsi/aic7xxx/aic7xxx_linux_host.h
drivers/scsi/aic7xxx/aic7xxx_linux_host.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/A/b/46_aic7xxx_li 1.2.1.1 644) renamed from drivers/scsi/aic7xxx/aic7xxx_linux_host.h Tue, 06 Nov 2001 15:52:09 -0500 root (linux/A/b/46_aic7xxx_li 1.2 644) +2 -1
@@ -89,7 +89,8 @@ present: 0, /* number of 7xxx's present */\ unchecked_isa_dma: 0, /* no memory DMA restrictions */\ use_clustering: ENABLE_CLUSTERING, \ - use_new_eh_code: 1 \ + use_new_eh_code: 1, \ + can_order: 1, \ } #endif /* _AIC7XXX_LINUX_HOST_H_ */
Index: linus.22/include/linux/reiserfs_fs_sb.h
include/linux/reiserfs_fs_sb.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/d/d/4_reiserfs_f 1.1.1.1.4.1.1.3 644) renamed from include/linux/reiserfs_fs_sb.h Fri, 11 Jan 2002 10:17:27 -0500 root (linux/d/d/4_reiserfs_f 1.1.1.1.4.1.1.2 644) +3 -20
@@ -175,20 +175,6 @@ #define JOURNAL_NUM_BITMAPS 5 /* number of copies of the bitmaps to have floating. Must be >= 2 */ #define JOURNAL_LIST_COUNT 64 -/* these are bh_state bit flag offset numbers, for use in the buffer head */ - -#define BH_JDirty 16 /* journal data needs to be written before buffer can be marked dirty */ -#define BH_JDirty_wait 18 /* commit is done, buffer marked dirty */ -#define BH_JNew 19 /* buffer allocated during this transaction, no need to write if freed during this trans too */ - -/* ugly. metadata blocks must be prepared before they can be logged. -** prepared means unlocked and cleaned. If the block is prepared, but not -** logged for some reason, any bits cleared while preparing it must be -** set again. -*/ -#define BH_JPrepared 20 /* block has been prepared for the log */ -#define BH_JRestore_dirty 22 /* restore the dirty bit later */ - /* One of these for every block in every transaction ** Each one is in two hash tables. First, a hash of the current transaction, and after journal_end, a ** hash of all the in memory transactions.
@@ -465,12 +451,8 @@ #define REISERFS_NO_BORDER 11 #define REISERFS_NO_UNHASHED_RELOCATION 12 #define REISERFS_HASHED_RELOCATION 13 -#define REISERFS_TEST4 14 - -#define REISERFS_TEST1 11 -#define REISERFS_TEST2 12 -#define REISERFS_TEST3 13 -#define REISERFS_TEST4 14 +#define REISERFS_BARRIER 14 +#define REISERFS_TEST4 15 #define reiserfs_r5_hash(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << FORCE_R5_HASH)) #define reiserfs_rupasov_hash(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << FORCE_RUPASOV_HASH))
@@ -479,6 +461,7 @@ #define reiserfs_no_border(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_NO_BORDER)) #define reiserfs_no_unhashed_relocation(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_NO_UNHASHED_RELOCATION)) #define reiserfs_hashed_relocation(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_HASHED_RELOCATION)) +#define reiserfs_barrier(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_BARRIER)) #define reiserfs_test4(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << REISERFS_TEST4)) #define dont_have_tails(s) ((s)->u.reiserfs_sb.s_mount_opt & (1 << NOTAIL))
Index: linus.22/include/linux/fs.h
include/linux/fs.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/e/d/3_fs.h 1.2.1.1.1.3.3.1.1.2.1.1 644) renamed from include/linux/fs.h Mon, 28 Jan 2002 09:51:50 -0500 root (linux/e/d/3_fs.h 1.2.1.1.1.3.3.1.1.2 644) +3 -0
@@ -219,6 +219,8 @@ BH_Wait_IO, /* 1 if we should write out this buffer */ BH_launder, /* 1 if we should throttle on this buffer */ BH_JBD, /* 1 if it has an attached journal_head */ + BH_Ordered, /* 1 if this buffer is a write barrier */ + BH_IO_OPNOTSUPP,/* 1 if block layer rejected a barrier write */ BH_PrivateStart,/* not a state bit, but the first bit available * for private allocation by other entities
@@ -279,6 +281,7 @@ #define buffer_mapped(bh) __buffer_state(bh,Mapped) #define buffer_new(bh) __buffer_state(bh,New) #define buffer_async(bh) __buffer_state(bh,Async) +#define buffer_ordered(bh) __buffer_state(bh,Ordered) #define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK)
Index: linus.22/include/linux/ide.h
include/linux/ide.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/f/d/28_ide.h 1.1.1.1 644) renamed from include/linux/ide.h Tue, 06 Nov 2001 15:47:39 -0500 root (linux/f/d/28_ide.h 1.1 644) +1 -0
@@ -371,6 +371,7 @@ byte dn; /* now wide spread use */ unsigned int failures; /* current failure count */ unsigned int max_failures; /* maximum allowed failure count */ + char special_buf[4]; /* IDE_DRIVE_CMD, free use */ } ide_drive_t; /*
Index: linus.22/include/linux/blkdev.h
include/linux/blkdev.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/h/d/14_blkdev.h 1.5 644) renamed from include/linux/blkdev.h Tue, 27 Nov 2001 23:34:21 -0500 root (linux/h/d/14_blkdev.h 1.4 644) +15 -0
@@ -29,6 +29,7 @@ kdev_t rq_dev; int cmd; /* READ or WRITE */ + unsigned long cmd_flags; int errors; unsigned long sector; unsigned long nr_sectors;
@@ -44,6 +45,10 @@ request_queue_t *q; }; +#define RQ_WRITE_ORDERED 1 /* ordered write */ +#define RQ_WRITE_PREFLUSH 2 /* pre-barrier flush */ +#define RQ_WRITE_POSTFLUSH 4 /* post-barrier flush */ + #include <linux/elevator.h> typedef int (merge_request_fn) (request_queue_t *q,
@@ -113,6 +118,11 @@ char head_active; /* + * ordered write support + */ + char ordered; + + /* * Is meant to protect the queue in the future instead of * io_request_lock */
@@ -124,6 +134,10 @@ wait_queue_head_t wait_for_request; }; +#define QUEUE_ORDERED_NONE 0 /* no support */ +#define QUEUE_ORDERED_TAG 1 /* supported by tags (fast) */ +#define QUEUE_ORDERED_FLUSH 2 /* supported by cache flush (ugh!) */ + struct blk_dev_struct { /* * queue_proc has to be atomic
@@ -160,6 +174,7 @@ extern void blk_init_queue(request_queue_t *, request_fn_proc *); extern void blk_cleanup_queue(request_queue_t *); extern void blk_queue_headactive(request_queue_t *, int); +extern void blk_queue_ordered(request_queue_t *, int); extern void blk_queue_make_request(request_queue_t *, make_request_fn *); extern void generic_unplug_device(void *);
Index: linus.22/include/linux/reiserfs_fs.h
include/linux/reiserfs_fs.h Wed, 13 Feb 2002 10:47:50 -0500 root (linux/k/d/7_reiserfs_f 1.2.2.1.2.1.1.1 644) renamed from include/linux/reiserfs_fs.h Thu, 13 Dec 2001 11:06:51 -0500 root (linux/k/d/7_reiserfs_f 1.2.2.1.2.1 644) +17 -0
@@ -1470,6 +1470,23 @@ extern task_queue reiserfs_commit_thread_tq ; extern wait_queue_head_t reiserfs_commit_thread_wait ; +enum bh_reiserfs_bits { + /* is in the current transaction */ + BH_JDirty = BH_PrivateStart, + /* is in a previous (perhaps uncommitted) transaction, waiting for + ** flush to disk + */ + BH_JDirty_wait, + /* was allocated during this transaction */ + BH_JNew, + /* cleaned and waited on, ready to be logged */ + BH_JPrepared, + /* dirty when prepared, if it doesn't get logged the dirty bit + ** must be restored + */ + BH_JRestore_Dirty, +} ; + /* biggest tunable defines are right here */ #define JOURNAL_BLOCK_COUNT 8192 /* number of blocks in the journal */ #define JOURNAL_MAX_BATCH 900 /* max blocks to batch into one transaction, don't make this any bigger than 900 */