|
|
| version 1.1, 2005/08/29 15:19:41 | version 1.2, 2005/09/07 15:13:10 |
|---|---|
| Line 2 | Line 2 |
| #include <linux/kernel.h> | #include <linux/kernel.h> |
| #include <linux/blkdev.h> | #include <linux/blkdev.h> |
| #include <linux/blktrace.h> | #include <linux/blktrace.h> |
| #include <linux/percpu.h> | |
| #include <linux/init.h> | |
| #include <asm/uaccess.h> | #include <asm/uaccess.h> |
| static DEFINE_PER_CPU(unsigned long long, blk_trace_cpu_offset) = { 0, }; | |
| static inline unsigned long long blk_trace_cpu_time(void) | |
| { | |
| unsigned long long offset; | |
| offset = sched_clock() - per_cpu(blk_trace_cpu_offset, get_cpu()); | |
| put_cpu(); | |
| return offset; | |
| } | |
| void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, | void __blk_add_trace(struct blk_trace *bt, sector_t sector, int bytes, |
| int rw, u32 what, int error, int pdu_len, char *pdu_data) | int rw, u32 what, int error, int pdu_len, char *pdu_data) |
| { | { |
| Line 25 void __blk_add_trace(struct blk_trace *b | Line 38 void __blk_add_trace(struct blk_trace *b |
| t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; | t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION; |
| t.sequence = atomic_add_return(1, &bt->sequence); | t.sequence = atomic_add_return(1, &bt->sequence); |
| t.time = sched_clock(); | t.time = blk_trace_cpu_time(); |
| t.sector = sector; | t.sector = sector; |
| t.bytes = bytes; | t.bytes = bytes; |
| t.action = what; | t.action = what; |
| t.pid = current->pid; | t.pid = current->pid; |
| t.cpu = raw_smp_processor_id(); | |
| t.error = error; | t.error = error; |
| t.pdu_len = pdu_len; | t.pdu_len = pdu_len; |
| Line 40 void __blk_add_trace(struct blk_trace *b | Line 54 void __blk_add_trace(struct blk_trace *b |
| local_irq_restore(flags); | local_irq_restore(flags); |
| } | } |
| static struct dentry *blk_tree_root; | |
| static DECLARE_MUTEX(blk_tree_mutex); | |
| static inline void blk_remove_root(void) | |
| { | |
| if (relayfs_remove_dir(blk_tree_root) != -ENOTEMPTY) | |
| blk_tree_root = NULL; | |
| } | |
| static void blk_remove_tree(struct dentry *dir) | |
| { | |
| down(&blk_tree_mutex); | |
| relayfs_remove_dir(dir); | |
| blk_remove_root(); | |
| up(&blk_tree_mutex); | |
| } | |
| static struct dentry *blk_create_tree(const char *blk_name) | |
| { | |
| struct dentry *dir = NULL; | |
| down(&blk_tree_mutex); | |
| if (!blk_tree_root) { | |
| blk_tree_root = relayfs_create_dir("block", NULL); | |
| if (!blk_tree_root) | |
| goto err; | |
| } | |
| dir = relayfs_create_dir(blk_name, blk_tree_root); | |
| if (!dir) | |
| blk_remove_root(); | |
| err: | |
| up(&blk_tree_mutex); | |
| return dir; | |
| } | |
| void blk_cleanup_trace(struct blk_trace *bt) | |
| { | |
| relay_close(bt->rchan); | |
| blk_remove_tree(bt->dir); | |
| kfree(bt); | |
| } | |
| int blk_stop_trace(struct block_device *bdev) | int blk_stop_trace(struct block_device *bdev) |
| { | { |
| request_queue_t *q = bdev_get_queue(bdev); | request_queue_t *q = bdev_get_queue(bdev); |
| Line 51 int blk_stop_trace(struct block_device * | Line 110 int blk_stop_trace(struct block_device * |
| down(&bdev->bd_sem); | down(&bdev->bd_sem); |
| spin_lock_irq(q->queue_lock); | |
| if (q->blk_trace) { | if (q->blk_trace) { |
| bt = q->blk_trace; | bt = q->blk_trace; |
| q->blk_trace = NULL; | q->blk_trace = NULL; |
| ret = 0; | ret = 0; |
| } | } |
| spin_unlock_irq(q->queue_lock); | |
| up(&bdev->bd_sem); | up(&bdev->bd_sem); |
| if (bt) { | if (bt) |
| relay_close(bt->rchan); | blk_cleanup_trace(bt); |
| kfree(bt); | |
| } | |
| return ret; | return ret; |
| } | } |
| Line 73 int blk_start_trace(struct block_device | Line 128 int blk_start_trace(struct block_device |
| { | { |
| request_queue_t *q = bdev_get_queue(bdev); | request_queue_t *q = bdev_get_queue(bdev); |
| struct blk_user_trace_setup buts; | struct blk_user_trace_setup buts; |
| struct blk_trace *bt; | struct blk_trace *bt = NULL; |
| struct dentry *dir = NULL; | |
| char b[BDEVNAME_SIZE]; | char b[BDEVNAME_SIZE]; |
| int ret = 0; | int ret; |
| if (!q) | if (!q) |
| return -ENXIO; | return -ENXIO; |
| Line 101 int blk_start_trace(struct block_device | Line 157 int blk_start_trace(struct block_device |
| if (!bt) | if (!bt) |
| goto err; | goto err; |
| ret = -ENOENT; | |
| dir = blk_create_tree(bdevname(bdev, b)); | |
| if (!dir) | |
| goto err; | |
| bt->dir = dir; | |
| atomic_set(&bt->sequence, 0); | atomic_set(&bt->sequence, 0); |
| bt->rchan = relay_open(bdevname(bdev, b), NULL, buts.buf_size, | |
| buts.buf_nr, NULL); | |
| ret = -EIO; | ret = -EIO; |
| bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, NULL); | |
| if (!bt->rchan) | if (!bt->rchan) |
| goto err; | goto err; |
| Line 113 int blk_start_trace(struct block_device | Line 174 int blk_start_trace(struct block_device |
| if (!bt->act_mask) | if (!bt->act_mask) |
| bt->act_mask = (u16) -1; | bt->act_mask = (u16) -1; |
| spin_lock_irq(q->queue_lock); | |
| q->blk_trace = bt; | q->blk_trace = bt; |
| spin_unlock_irq(q->queue_lock); | up(&bdev->bd_sem); |
| ret = 0; | return 0; |
| err: | err: |
| up(&bdev->bd_sem); | up(&bdev->bd_sem); |
| if (dir) | |
| blk_remove_tree(dir); | |
| if (bt) | |
| kfree(bt); | |
| return ret; | return ret; |
| } | } |
| static void blk_trace_check_cpu_time(void *data) | |
| { | |
| unsigned long long a, b, *t; | |
| struct timeval tv; | |
| int cpu = get_cpu(); | |
| t = &per_cpu(blk_trace_cpu_offset, cpu); | |
| a = sched_clock(); | |
| do_gettimeofday(&tv); | |
| b = sched_clock(); | |
| *t = tv.tv_sec * 1000000000 + tv.tv_usec * 1000; | |
| *t -= (a + b) / 2; | |
| put_cpu(); | |
| } | |
| static int blk_trace_calibrate_offsets(void) | |
| { | |
| unsigned long flags; | |
| smp_call_function(blk_trace_check_cpu_time, NULL, 1, 1); | |
| local_irq_save(flags); | |
| blk_trace_check_cpu_time(NULL); | |
| local_irq_restore(flags); | |
| return 0; | |
| } | |
| static __init int blk_trace_init(void) | |
| { | |
| return blk_trace_calibrate_offsets(); | |
| } | |
| module_init(blk_trace_init); | |