/* * Copyright (C) 2001-2003 Sistina Software (UK) Limited. * * This file is released under the GPL. */ #include "dm.h" #include #include #include #include #include #include #include #include #include #include #include #include #define DM_MSG_PREFIX "statistic" #define STATISTIC_IOC_MAGIC 0xa1 #define RESET_TIME _IO(STATISTIC_IOC_MAGIC, 0) #define RESET_SPEED _IOW(STATISTIC_IOC_MAGIC, 1, unsigned long) #define STATISTIC_IOC_MAXNR 2 static unsigned long write_per_sec = 1; /* One bio per second by default */ module_param(write_per_sec, ulong, 0); /* * statistic: maps a statistic range of a device. */ struct statistic_c { struct dm_dev *dev; sector_t start; }; static struct mutex lock; static struct list_head head; static struct list_head head_write; struct bio_data { struct list_head list; unsigned long bio_time; struct bio bio_value; }; unsigned long base_time; static void *statistic_seq_start(struct seq_file *s, loff_t *pos) { mutex_lock(&lock); return seq_list_start(&head, *pos); } static void *statistic_seq_next(struct seq_file *s, void *v, loff_t *pos) { return seq_list_next(v, &head, pos); } static void *statistic_seq_stop(struct seq_file *s, void *v) { mutex_unlock(&lock); return NULL; } static int statistic_seq_show(struct seq_file *s, void *v) { struct bio_data *data = list_entry(v, struct bio_data, list); seq_printf(s, "struct bio:\n"); //seq_printf(s, "bi_cnt=%d\n", data->bio_value.bi_cnt); seq_printf(s, "time: %lu\n", data->bio_time); seq_printf(s, "Read or Write %lx\n", data->bio_value.bi_rw & 0x0001); seq_printf(s, "\n"); return 0; } static struct seq_operations seq_ops = { .start = statistic_seq_start, .next = statistic_seq_next, .stop = statistic_seq_stop, .show = statistic_seq_show }; static int statistic_open(struct inode *inode, struct file *file) { return (seq_open(file, &seq_ops)); } static struct file_operations seq_fops = { .owner = THIS_MODULE, .open = statistic_open, .read = seq_read, //.write = seq_write, .llseek = seq_lseek, .release = seq_release, }; /* * Construct a statistic mapping: */ static int statistic_ctr(struct dm_target *ti, unsigned int argc, char **argv) { struct statistic_c *lc; unsigned long long tmp; char dummy; if (argc != 2) { ti->error = "Invalid argument count"; return -EINVAL; } lc = kmalloc(sizeof(*lc), GFP_KERNEL); if (lc == NULL) { ti->error = "dm-statistic: Cannot allocate statistic context"; return -ENOMEM; } if (sscanf(argv[1], "%llu%c", &tmp, &dummy) != 1) { ti->error = "dm-statistic: Invalid device sector"; goto bad; } lc->start = tmp; if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &lc->dev)) { ti->error = "dm-statistic: Device lookup failed"; goto bad; } ti->num_flush_requests = 1; ti->num_discard_requests = 1; ti->num_write_same_requests = 1; ti->private = lc; return 0; bad: kfree(lc); return -EINVAL; } static void statistic_dtr(struct dm_target *ti) { struct statistic_c *lc = (struct statistic_c *) ti->private; dm_put_device(ti, lc->dev); kfree(lc); } static sector_t statistic_map_sector(struct dm_target *ti, sector_t bi_sector) { struct statistic_c *lc = ti->private; return lc->start + dm_target_offset(ti, bi_sector); } static void add_bio(struct bio *bio) { unsigned long current_time = jiffies - base_time; struct bio_data *data; mutex_lock(&lock); data = (struct bio_data *)kmalloc(sizeof(*data), GFP_KERNEL); if (data != NULL) { data->bio_time = current_time; data->bio_value = *bio; list_add(&data->list, &head); if (data->bio_value.bi_rw & 0x0001) /* add the bio to the list if it is a write bio*/ list_add(&data->list, &head_write); } mutex_unlock(&lock); } static void bio_statistic(struct bio *bio) { //int recent_bio; struct bio_data *data; if ((bio->bi_rw & 0x0001) && write_per_sec!=0) { if (!list_empty(&head_write)) { data = list_entry((&head_write)->next, struct bio_data, list); if (jiffies-base_time-data->bio_timebio_time)); } } } add_bio(bio); //if (entry) //entry->proc_fops = &seq_fops; printk(KERN_INFO "struct bio\n"); printk(KERN_INFO "bi_flags=%lx\n", bio->bi_flags); printk(KERN_INFO "bi_rw=%lx\n", bio->bi_rw); //printk(KERN_INFO "bi_cnt=%d\n", bio->bi_cnt); printk(KERN_INFO "\n"); } static void clean_all(struct list_head *head) { struct bio_data *data; while (!list_empty(head)) { data = list_entry(head->next, struct bio_data, list); list_del(&data->list); kfree(data); } } static void statistic_map_bio(struct dm_target *ti, struct bio *bio) { struct statistic_c *lc = ti->private; bio_statistic(bio); //printk(KERN_INFO "In statistic_map_bio\nTestTestTest\n"); bio->bi_bdev = lc->dev->bdev; if (bio_sectors(bio)) bio->bi_sector = statistic_map_sector(ti, bio->bi_sector); } static int statistic_map(struct dm_target *ti, struct bio *bio) { statistic_map_bio(ti, bio); return DM_MAPIO_REMAPPED; } static void statistic_status(struct dm_target *ti, status_type_t type, unsigned status_flags, char *result, unsigned maxlen) { struct statistic_c *lc = (struct statistic_c *) ti->private; switch (type) { case STATUSTYPE_INFO: result[0] = '\0'; break; case STATUSTYPE_TABLE: snprintf(result, maxlen, "%s %llu", lc->dev->name, (unsigned long long)lc->start); break; } } static void statistic_reset_time(void) { base_time = jiffies; clean_all(&head); clean_all(&head_write); } EXPORT_SYMBOL_GPL(statistic_reset_time); static void statistic_reset_speed(unsigned long arg) { write_per_sec = arg; } EXPORT_SYMBOL_GPL(statistic_reset_speed); static int statistic_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg) { struct statistic_c *lc = (struct statistic_c *) ti->private; struct dm_dev *dev = lc->dev; int r = 0; switch (cmd) { case RESET_TIME: statistic_reset_time(); break; case RESET_SPEED: statistic_reset_speed(arg); break; default: break; } /* * Only pass ioctls through if the device sizes match exactly. */ if (lc->start || ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT) r = scsi_verify_blk_ioctl(NULL, cmd); return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg); } static int statistic_merge(struct dm_target *ti, struct bvec_merge_data *bvm, struct bio_vec *biovec, int max_size) { struct statistic_c *lc = ti->private; struct request_queue *q = bdev_get_queue(lc->dev->bdev); if (!q->merge_bvec_fn) return max_size; bvm->bi_bdev = lc->dev->bdev; bvm->bi_sector = statistic_map_sector(ti, bvm->bi_sector); return min(max_size, q->merge_bvec_fn(q, bvm, biovec)); } static int statistic_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data) { struct statistic_c *lc = ti->private; return fn(ti, lc->dev, lc->start, ti->len, data); } static struct target_type statistic_target = { .name = "statistic", .version = {0, 1, 0}, .module = THIS_MODULE, .ctr = statistic_ctr, .dtr = statistic_dtr, .map = statistic_map, .status = statistic_status, .ioctl = statistic_ioctl, .merge = statistic_merge, .iterate_devices = statistic_iterate_devices, }; int jiffies_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len; if (off > 0) { *eof = 1; return 0; } //len += sprintf(page, "%lu\n", base_time); len = sprintf(page, "HZ=%d\nbase_time=%lu\ncurrent_jiffies=%lu\n",HZ, base_time, jiffies); return len; } static int proc_init(void) { struct proc_dir_entry *entry, *jiffies_info; mutex_init(&lock); INIT_LIST_HEAD(&head); INIT_LIST_HEAD(&head_write); entry = create_proc_entry("statistic", S_IWUSR | S_IRUGO, NULL); if (entry == NULL) { clean_all(&head); return -ENOMEM; } base_time = jiffies; entry->proc_fops = &seq_fops; jiffies_info = create_proc_entry("jiffies", S_IWUSR | S_IRUGO, NULL); if (jiffies_info == NULL) { return -ENOMEM; } jiffies_info->read_proc = jiffies_read; return 0; } int __init dm_statistic_init(void) { int r = dm_register_target(&statistic_target); if (r < 0) DMERR("register failed %d", r); proc_init(); return r; } static void proc_exit(void) { remove_proc_entry("jiffies", NULL); remove_proc_entry("statistic", NULL); clean_all(&head); clean_all(&head_write); } void dm_statistic_exit(void) { proc_exit(); dm_unregister_target(&statistic_target); }