# This is a BitKeeper generated patch for the following project: # Project Name: Linux kernel tree # This patch format is intended for GNU patch command version 2.5 or higher. # This patch includes the following deltas: # ChangeSet 1.780.1.1 -> 1.782 # include/linux/loop.h 1.5 -> 1.6 # drivers/block/loop.c 1.22.1.2 -> 1.25 # drivers/block/Makefile 1.4 -> 1.6 # (new) -> 1.6 drivers/block/testdrive.c # (new) -> 1.1 include/linux/testdrive.h # # The following is the BitKeeper ChangeSet Log # -------------------------------------------- # 02/11/11 sct@sisko.scot.redhat.com 1.782 # Merge sisko.scot.redhat.com:/net/u6/src/linux-2.4 # into sisko.scot.redhat.com:/net/u6/src/linux-testdrive-2.4 # -------------------------------------------- # --- linux-2.4.21-pre3-ext3merge/drivers/block/Makefile.=K0027=.orig 2003-01-21 22:45:20.000000000 +0000 +++ linux-2.4.21-pre3-ext3merge/drivers/block/Makefile 2003-01-21 22:46:53.000000000 +0000 @@ -10,9 +10,9 @@ O_TARGET := block.o -export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o +export-objs := ll_rw_blk.o blkpg.o loop.o DAC960.o genhd.o testdrive.o -obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o +obj-y := ll_rw_blk.o blkpg.o genhd.o elevator.o obj-$(CONFIG_MAC_FLOPPY) += swim3.o obj-$(CONFIG_BLK_DEV_FD) += floppy.o @@ -23,7 +23,7 @@ obj-$(CONFIG_ATARI_SLM) += acsi_slm.o obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o obj-$(CONFIG_BLK_DEV_RAM) += rd.o -obj-$(CONFIG_BLK_DEV_LOOP) += loop.o +obj-$(CONFIG_BLK_DEV_LOOP) += loop.o testdrive.o obj-$(CONFIG_BLK_DEV_PS2) += ps2esdi.o obj-$(CONFIG_BLK_DEV_XD) += xd.o obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o --- linux-2.4.21-pre3-ext3merge/drivers/block/loop.c.=K0027=.orig 2003-01-21 22:45:20.000000000 +0000 +++ linux-2.4.21-pre3-ext3merge/drivers/block/loop.c 2003-01-21 22:46:53.000000000 +0000 @@ -75,6 +75,7 @@ #include #include +#include #define MAJOR_NR LOOP_MAJOR @@ -393,6 +394,7 @@ if (!uptodate || test_bit(BH_Dirty, &bh->b_state)) { struct buffer_head *rbh = bh->b_private; + td_end_io(lo, rbh, uptodate); rbh->b_end_io(rbh, uptodate); if (atomic_dec_and_test(&lo->lo_pending)) up(&lo->lo_bh_mutex); @@ -463,7 +465,8 @@ struct buffer_head *bh = NULL; struct loop_device *lo; unsigned long IV; - + int err; + if (!buffer_locked(rbh)) BUG(); @@ -489,6 +492,10 @@ rbh = blk_queue_bounce(q, rw, rbh); + err = td_start_io(lo, rw, rbh); + if (err) + goto err; + /* * file backed, queue for loop_thread to handle */ @@ -541,6 +548,7 @@ int rw = !!test_and_clear_bit(BH_Dirty, &bh->b_state); ret = do_bh_filebacked(lo, bh, rw); + td_end_io(lo, bh, !ret); bh->b_end_io(bh, !ret); } else { struct buffer_head *rbh = bh->b_private; @@ -549,6 +557,7 @@ ret = lo_do_transfer(lo, READ, bh->b_data, rbh->b_data, bh->b_size, IV); + td_end_io(lo, rbh, !ret); rbh->b_end_io(rbh, !ret); loop_put_buffer(bh); } @@ -693,6 +702,7 @@ set_blocksize(dev, bs); lo->lo_bh = lo->lo_bhtail = NULL; + setup_testdrive(lo); kernel_thread(loop_thread, lo, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); down(&lo->lo_sem); @@ -759,6 +769,7 @@ lo->lo_backing_file = NULL; loop_release_xfer(lo); + teardown_testdrive(lo); lo->transfer = NULL; lo->ioctl = NULL; lo->lo_device = 0; --- /dev/null 2002-08-31 00:31:37.000000000 +0100 +++ linux-2.4.21-pre3-ext3merge/drivers/block/testdrive.c 2003-01-21 22:46:53.000000000 +0000 @@ -0,0 +1,373 @@ +/* + * linux/drivers/block/testdrive.c + * + * Written by Stephen C. Tweedie, 2002 + * + * Copyright Red Hat, Inc. 2002 + * Redistribution of this file is permitted under the GNU General Public + * License. + * +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static int enable_testdrive = 1; +static int enable_trace = 0; +static int enable_faults = 0; +static int fault_writes = 1; +static int fault_reads = 1; +static int fault_freq = 1000; +static struct list_head *bh_hash_map; +static struct list_head *sector_hash_map; +static kmem_cache_t *td_cache; + +static DECLARE_MUTEX(testdrive_init); +static spinlock_t hash_lock = SPIN_LOCK_UNLOCKED; + +/* Reuse the buffer hash function from buffer.c */ +#define NR_HASH 4096 +static int hash_shift; + +/* + * "Minimal standard" pseudo-random RNG. + * + * Domain is (0, MAXINT). Don't use 0 as a seed! + */ + +static int seed = 1; + +static int prandom(void) +{ + int hi, lo, t; + + hi = seed / 127773; + lo = seed % 127773; + t = 16807 * lo - 2836 * hi; + if (t <= 0) + t += 0x7fffffff; + seed = t; + return t; +} + +/* After several hours of tedious analysis, the following hash + * function won. Do not mess with it... -DaveM + */ +static inline int sector_hash_fn(kdev_t dev, unsigned long sector) +{ + unsigned int hash; + hash = (((dev<<(hash_shift - 6)) ^ (dev<<(hash_shift - 9))) ^ + (((sector)<<(hash_shift - 6)) ^ (sector >> 13) ^ + (sector << (hash_shift - 12)))); + return hash & (NR_HASH-1); +} + +static inline int bh_hash_fn(struct buffer_head *bh) +{ + unsigned int hash; + hash = (unsigned int) bh; + hash = (hash >> 3) ^ (hash >> 6) ^ (hash >> 9); + return hash & (NR_HASH-1); +} + +struct td_io_entry +{ + struct list_head bh_list; /* hash links for &bh lookup*/ + struct list_head sector_list; /* hash links for sector-id lookup */ + struct buffer_head *bh; /* bh which we are tracking */ + unsigned long o_rsector; /* Copy of original sector and */ + kdev_t o_rdev; /* device used to hash this buffer */ +}; + + +int init_testdrive(void) +{ + int err = 0; + + down(&testdrive_init); + if (!bh_hash_map) { + unsigned int tmp; + + tmp = NR_HASH; + hash_shift = 0; + while((tmp >>= 1UL) != 0UL) + hash_shift++; + + bh_hash_map = vmalloc(2 * NR_HASH * sizeof(*bh_hash_map)); + if (!bh_hash_map) + err = -ENOMEM; + else { + int i; + for (i = 0; i < 2 * NR_HASH; i++) + INIT_LIST_HEAD(&bh_hash_map[i]); + } + sector_hash_map = &bh_hash_map[NR_HASH]; + printk(KERN_INFO "Initialised testdrive hash map: " + "%d entries at %p\n", NR_HASH, bh_hash_map); + } + + if (bh_hash_map && !td_cache) { + td_cache = kmem_cache_create("testdrive_io", + sizeof(struct td_io_entry), 0, + 0, NULL, NULL); + if (!td_cache) + err = -ENOMEM; + } + + /* Make sure any module-param seed is valid: +ve, and not 0 or + MAXINT. */ + seed &= 0x7fffffff; + if (seed == 0 || seed == 0x7fffffff) + seed = 1; + + up(&testdrive_init); + return err; +} + +static void exit_testdrive(void) +{ + if (bh_hash_map) + vfree(bh_hash_map); + if (td_cache) + kmem_cache_destroy(td_cache); +} + + +int setup_testdrive(struct loop_device *lo) +{ + struct testdrive_dev *td = &lo->lo_testdrive; + int err = 0; + + if (!enable_testdrive) + goto out; + + err = init_testdrive(); + if (err) + goto out; + + set_bit(TD_STATUS_INITIALISED, &td->flags); + + MOD_INC_USE_COUNT; + + lo->lo_flags &= ~LO_FLAGS_BH_REMAP; +out: + return err; +} + +void teardown_testdrive(struct loop_device *lo) +{ + MOD_DEC_USE_COUNT; +} + + +static inline struct td_io_entry * +find_bh(struct list_head *hash, struct buffer_head *bh) +{ + struct list_head *tmp; + + tmp = hash->next; + while (tmp != hash) { + struct td_io_entry *td; + td = list_entry(tmp, struct td_io_entry, bh_list); + if (bh == td->bh) + return td; + + tmp = tmp->next; + } + return NULL; +} + +static inline struct td_io_entry * +find_bh_location(struct list_head *hash, kdev_t dev, unsigned long sector) +{ + struct list_head *tmp; + + tmp = hash->next; + while (tmp != hash) { + struct td_io_entry *td; + td = list_entry(tmp, struct td_io_entry, sector_list); + if (td->o_rdev == dev && + td->o_rsector == sector) + return td; + + tmp = tmp->next; + } + return NULL; +} + +static int try_inject_fault(struct loop_device *lo, + int rw, struct buffer_head *bh) +{ + static int time_to_next_fault = 0; + + if (rw == READ) { + if (!fault_reads) + return 0; + } else { + if (!fault_writes) + return 0; + } + + if (!time_to_next_fault) + time_to_next_fault = (prandom() % fault_freq) + 1; + + return (--time_to_next_fault <= 0); +} + +int td_start_io(struct loop_device *lo, + int rw, struct buffer_head *bh) +{ + struct buffer_head *bh_collide; + struct list_head *s_hash, *bh_hash; + struct td_io_entry *td, *td_collide; + unsigned long flags; + + if (!test_bit(TD_STATUS_INITIALISED, &lo->lo_testdrive.flags)) + return 0; + + if (enable_trace) + printk(KERN_DEBUG "%s: starting bh %p: %c (%#06x, %lu) \n", + __FUNCTION__, bh, + rw == WRITE ? 'w' : 'r', + bh->b_rdev, bh->b_rsector); + + if (enable_faults) + if (try_inject_fault(lo, rw, bh)) { + printk(KERN_DEBUG "%s: forcing IO error on bh %p\n", + __FUNCTION__, bh); + /* loop.c will handle the bh completion on error. */ + return 1; + } + + + s_hash = §or_hash_map[sector_hash_fn(bh->b_rdev, bh->b_rsector)]; + bh_hash = &bh_hash_map[bh_hash_fn(bh)]; + + td = kmem_cache_alloc(td_cache, SLAB_ATOMIC); + if (!td) { + printk(KERN_WARNING "%s: could not allocate td_io_entry for %p.\n", + __FUNCTION__, bh); + return 0; + } + + /* Simple progress monitor just to see if anything is happening */ + { + static unsigned int target = 1; + static unsigned int total = 0; + ++total; + if (total == target) { + printk(KERN_DEBUG "testdrive: started %d ios.\n", total); + target <<= 1; + } + } + + spin_lock_irqsave(&hash_lock, flags); + + td_collide = find_bh_location(s_hash, bh->b_rdev, bh->b_rsector); + if (td_collide) { + bh_collide = td_collide->bh; + printk(KERN_WARNING "%s: found IO dup of buffer %p at %p: " + "device %#04x, sector %lu, blocknr %lu\n", + __FUNCTION__, bh, bh_collide, + bh->b_rdev, bh->b_rsector, bh->b_blocknr); + } + + td_collide = find_bh(bh_hash, bh); + if (td_collide) { + printk(KERN_WARNING "%s: buffer %p submitted twice!\n", + __FUNCTION__, bh); + BUG(); + } + + td->bh = bh; + td->o_rsector = bh->b_rsector; + td->o_rdev = bh->b_rdev; + list_add(&td->sector_list, s_hash); + list_add(&td->bh_list, bh_hash); + spin_unlock_irqrestore(&hash_lock, flags); + return 0; +} + + +void td_end_io(struct loop_device *lo, struct buffer_head *bh, int uptodate) +{ + struct list_head *hash; + struct td_io_entry *td; + unsigned long flags; + + if (!test_bit(TD_STATUS_INITIALISED, &lo->lo_testdrive.flags)) + return; + + if (enable_trace) + printk(KERN_DEBUG "%s: completing bh %p (%d)\n", + __FUNCTION__, bh, uptodate); + + hash = &bh_hash_map[bh_hash_fn(bh)]; + spin_lock_irqsave(&hash_lock, flags); + + td = find_bh(hash, bh); + if (td){ + list_del(&td->bh_list); + list_del(&td->sector_list); + } else { + printk(KERN_WARNING "%s: could not find td_io_entry for buffer %p\n", + __FUNCTION__, bh); + } + + spin_unlock_irqrestore(&hash_lock, flags); + if (td) + kmem_cache_free(td_cache, td); +} + +/* These are only exported for the use of loop.o */ +EXPORT_SYMBOL_GPL(setup_testdrive); +EXPORT_SYMBOL_GPL(teardown_testdrive); +EXPORT_SYMBOL_GPL(td_start_io); +EXPORT_SYMBOL_GPL(td_end_io); + +module_exit(exit_testdrive); + +MODULE_PARM(enable_testdrive, "i"); +MODULE_PARM_DESC(enable_testdrive, "Set to 0 to disable the testdrive testing functionality"); + +MODULE_PARM(enable_trace, "i"); +MODULE_PARM_DESC(enable_trace, "Set to 1 to enable IO tracing on testdrive loop devices"); + +MODULE_PARM(enable_faults, "i"); +MODULE_PARM_DESC(enable_faults, "Set to 1 to enable random EIO injection"); + +MODULE_PARM(fault_writes, "i"); +MODULE_PARM_DESC(fault_writes, "Set to 0 to disable EIO injection on writes"); + +MODULE_PARM(fault_reads, "i"); +MODULE_PARM_DESC(fault_reads, "Set to 0 to disable EIO injection on reads"); + +MODULE_PARM(fault_freq, "i"); +MODULE_PARM_DESC(fault_freq, "Frequency of IO failure injection"); + +MODULE_PARM(seed, "i"); +MODULE_PARM_DESC(seed, "Random number seed for EIO injection"); + +MODULE_LICENSE("GPL"); --- linux-2.4.21-pre3-ext3merge/include/linux/loop.h.=K0027=.orig 2003-01-21 22:45:20.000000000 +0000 +++ linux-2.4.21-pre3-ext3merge/include/linux/loop.h 2003-01-21 22:46:53.000000000 +0000 @@ -2,6 +2,7 @@ #define _LINUX_LOOP_H #include +#include /* * include/linux/loop.h @@ -56,6 +57,7 @@ struct semaphore lo_ctl_mutex; struct semaphore lo_bh_mutex; atomic_t lo_pending; + struct testdrive_dev lo_testdrive; }; typedef int (* transfer_proc_t)(struct loop_device *, int cmd, @@ -122,7 +124,8 @@ #define LO_CRYPT_IDEA 6 #define LO_CRYPT_DUMMY 9 #define LO_CRYPT_SKIPJACK 10 -#define MAX_LO_CRYPT 20 +#define LO_CRYPT_PRIVATE 20 +#define MAX_LO_CRYPT 30 #ifdef __KERNEL__ /* Support for loadable transfer modules */ --- /dev/null 2002-08-31 00:31:37.000000000 +0100 +++ linux-2.4.21-pre3-ext3merge/include/linux/testdrive.h 2003-01-21 22:46:53.000000000 +0000 @@ -0,0 +1,25 @@ +#ifndef _LINUX_TESTDRIVE_H +#define _LINUX_TESTDRIVE_H + +struct loop_device; + +struct testdrive_dev +{ + unsigned long flags; + +}; + +/* Testdrive device flags: */ +enum { + TD_STATUS_INITIALISED +}; + +int setup_testdrive(struct loop_device *); +void teardown_testdrive(struct loop_device *); + +int td_start_io(struct loop_device *, int rw, struct buffer_head *); +void td_end_io(struct loop_device *, struct buffer_head *, int uptodate); + +// #define LOOP_GET_STATUS 0x4C03 + +#endif