--- diff/drivers/md/dm-recorder.c 1970-01-01 01:00:00.000000000 +0100 +++ source/drivers/md/dm-recorder.c 2004-02-10 14:45:45.000000000 +0000 @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2004 Red Hat Inc + * + * This file is released under the GPL. + */ + +#include "dm.h" + +#include +#include +#include +#include +#include + +/* + * The recorder target is designed to be layered over another + * block device, it also writes to a seperate log device. It + * simply records details about every io to the origin device + * (ie. time, location, size). The actual data in the ios is not + * recorded. This should give us a way a generating more + * realistic test scenarios. Once the log device is full an + * event is triggered, and logging stops. If the system crashes + * some log data will be lost. + */ + +/*---------------------------------------------------------------- + * On disk structures + * FIXME: we can come up with a much more space efficient format. + *--------------------------------------------------------------*/ +struct header { + u32_t magic; + u32_t format_version; + u64_t sector_start; + u32_t hz; +}; + +struct atom { + u64_t time; + u64_t sector; + u32_t write; + u32_t length; +}; + +/*---------------------------------------------------------------- + * Recorder target + *--------------------------------------------------------------*/ +#define NR_SECTORS 256 +#define NR_ATOMS ((NR_SECTORS << SECTOR_SHIFT) / sizeof(struct atom)) +struct recorder { + struct dm_dev *origin; + struct dm_dev *log; + + sector_t log_cursor; + + unsigned current_atom; + struct atom *atoms; +}; + +#define ESTR(x) ("dm-recorder: " x) + +/* + * Construct a recorder mapping: + */ +static int recorder_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct recorder *rc; + + if (argc != 2) { + ti->error = ESTR("invalid argument count"); + return -EINVAL; + } + + rc = kmalloc(sizeof(*rc), GFP_KERNEL); + if (!rc) { + ti->error = ESTR("cannot allocate linear context"); + return -ENOMEM; + } + + if (dm_get_device(ti, argv[0], rc->start, ti->len, + dm_table_get_mode(ti->table), &rc->origin)) { + ti->error = ESTR("origin device lookup failed"); + goto bad1; + } + + /* + * We don't mind how big the log is, as long as it has + * room for the header. + */ + if (dm_get_device(ti, argv[1], rc->start, 1, + dm_table_get_mode(ti->table), &rc->log)) { + ti->error = ESTR("log device lookup failed"); + goto bad2; + } + + rc->log_cursor = 1; + rc->current_atom = 0; + rc->atoms = dm_vcalloc(NR_ATOMS, sizeof(*rc->atoms)); + if (!rc->atoms) { + ti->error = ESTR("couln't allocate atoms array"); + goto bad3; + } + + ti->private = rc; + return 0; + + bad3: + dm_put_device(ti, rc->log); + bad2: + dm_put_device(ti, rc->origin); + bad1: + kfree(rc); + return -EINVAL; +} + +static void recorder_dtr(struct dm_target *ti) +{ + struct recorder *rc = (struct recorder *) ti->private; + + vfree(rc->atoms); + dm_put_device(ti, rc->log); + dm_put_device(ti, rc->origin); + kfree(rc); +} + +static int write_atoms(struct recorder *rc) +{ + int r; + unsigned long errors; + struct io_region where; + + where.bdev = rc->log; + where.sector = current_sector; + where.count = NR_SECTORS; + + r = dm_io_sync(1, &where, WRITE, pages, 0, errors); + if (!r) { + rc->current_atom = 0; + rc->current_sector += NR_SECTORS; + } + + /* FIXME: detect end of log device, and trigger event if neccessary */ + + return r; +} + +static void atom_to_disk(struct atom *a) +{ + a->time = cpu_to_le64(a->time); + a->sector = cpu_to_le64(a->sector); + a->write = cpu_to_le32(a->write); + a->length = cpu_to_le32(a->length); +} + +static u64_t now(void) +{ + +} + +static int recorder_map(struct dm_target *ti, struct bio *bio, + union map_info *map_context) +{ + struct recorder *rc = (struct recorder *) ti->private; + struct atom *a = rc->atoms + rc->current_atom++; + + /* fill in the atom */ + a->time = now(); + a->write = test_bit(BIO_RW, bio->bi_rw); + a->sector = bio->bi_sector; + a->length = to_sector(bio->bi_size); + atom_to_disk(a); + + if (rc->current_atom = NR_ATOMS) + write_atoms(rc); + + bio->bi_bdev = rc->origin->bdev; + return 1; +} + +static void recorder_suspend(struct dm_target *ti) +{ + struct recorder *rc = (struct recorder *) ti->private; +} + +static void recorder_resume(struct dm_target *ti) +{ + struct recorder *rc = (struct recorder *) ti->private; + +} + +static int recorder_status(struct dm_target *ti, status_type_t type, + char *result, unsigned int maxlen) +{ + struct linear_c *lc = (struct linear_c *) ti->private; + char origin_buffer[32], log_buffer[32]; + + switch (type) { + case STATUSTYPE_INFO: + result[0] = '\0'; + break; + + case STATUSTYPE_TABLE: + format_dev_t(origin_buffer, rc->origin_dev->bdev->bd_dev); + format_dev_t(log_buffer, rc->log_dev->bdev->bd_dev); + snprintf(result, maxlen, "%s %s", origin_buffer, log_buffer); + break; + } + return 0; +} + +static struct target_type recorder_target = { + .name = "recorder", + .version= {1, 0, 0}, + .module = THIS_MODULE, + .ctr = recorder_ctr, + .dtr = recorder_dtr, + .map = recorder_map, + .suspend = recorder_suspend, + .resume = recorder_resume, + .status = recorder_status, +}; + +int __init dm_recorder_init(void) +{ + int r = dm_register_target(&linear_target); + + if (r < 0) + DMERR(ESTR("register target failed %d", r)); + + return r; +} + +void __exit dm_recorder_exit(void) +{ + int r = dm_unregister_target(&linear_target); + + if (r < 0) + DMERR(ESTR("unregister target failed %d", r)); +} + +module_init(dm_recorder_init); +module_exit(dm_recorder_exit); + +MODULE_DESCRIPTION(DM_NAME " mirror target"); +MODULE_AUTHOR("Joe Thornber"); +MODULE_LICENSE("GPL");