File: [Development] / linux-2.6-xfs / drivers / block / Attic / blktrace.c (download)
Revision 1.2, Wed Sep 7 15:13:10 2005 UTC (12 years, 1 month ago) by nathans.longdrop.melbourne.sgi.com
Branch: MAIN
Changes since 1.1: +114 -14
lines
Merge in rev3 of blktrace patch from Jens Axboe.
Merge of 2.6.x-xfs-melb:linux:23729a by kenmcd.
|
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/blkdev.h>
#include <linux/blktrace.h>
#include <linux/percpu.h>
#include <linux/init.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,
int rw, u32 what, int error, int pdu_len, char *pdu_data)
{
struct blk_io_trace t;
unsigned long flags;
if (rw & (1 << BIO_RW_BARRIER))
what |= BLK_TC_ACT(BLK_TC_BARRIER);
if (rw & (1 << BIO_RW_SYNC))
what |= BLK_TC_ACT(BLK_TC_SYNC);
if (rw & WRITE)
what |= BLK_TC_ACT(BLK_TC_WRITE);
else
what |= BLK_TC_ACT(BLK_TC_READ);
if (((bt->act_mask << BLK_TC_SHIFT) & what) == 0)
return;
t.magic = BLK_IO_TRACE_MAGIC | BLK_IO_TRACE_VERSION;
t.sequence = atomic_add_return(1, &bt->sequence);
t.time = blk_trace_cpu_time();
t.sector = sector;
t.bytes = bytes;
t.action = what;
t.pid = current->pid;
t.cpu = raw_smp_processor_id();
t.error = error;
t.pdu_len = pdu_len;
local_irq_save(flags);
__relay_write(bt->rchan, &t, sizeof(t));
if (pdu_len)
__relay_write(bt->rchan, pdu_data, pdu_len);
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)
{
request_queue_t *q = bdev_get_queue(bdev);
struct blk_trace *bt = NULL;
int ret = -EINVAL;
if (!q)
return -ENXIO;
down(&bdev->bd_sem);
if (q->blk_trace) {
bt = q->blk_trace;
q->blk_trace = NULL;
ret = 0;
}
up(&bdev->bd_sem);
if (bt)
blk_cleanup_trace(bt);
return ret;
}
int blk_start_trace(struct block_device *bdev, char __user *arg)
{
request_queue_t *q = bdev_get_queue(bdev);
struct blk_user_trace_setup buts;
struct blk_trace *bt = NULL;
struct dentry *dir = NULL;
char b[BDEVNAME_SIZE];
int ret;
if (!q)
return -ENXIO;
if (copy_from_user(&buts, arg, sizeof(buts)))
return -EFAULT;
if (!buts.buf_size || !buts.buf_nr)
return -EINVAL;
strcpy(buts.name, bdevname(bdev, b));
if (copy_to_user(arg, &buts, sizeof(buts)))
return -EFAULT;
down(&bdev->bd_sem);
ret = -EBUSY;
if (q->blk_trace)
goto err;
ret = -ENOMEM;
bt = kmalloc(sizeof(*bt), GFP_KERNEL);
if (!bt)
goto err;
ret = -ENOENT;
dir = blk_create_tree(bdevname(bdev, b));
if (!dir)
goto err;
bt->dir = dir;
atomic_set(&bt->sequence, 0);
ret = -EIO;
bt->rchan = relay_open("trace", dir, buts.buf_size, buts.buf_nr, NULL);
if (!bt->rchan)
goto err;
bt->act_mask = buts.act_mask;
if (!bt->act_mask)
bt->act_mask = (u16) -1;
q->blk_trace = bt;
up(&bdev->bd_sem);
return 0;
err:
up(&bdev->bd_sem);
if (dir)
blk_remove_tree(dir);
if (bt)
kfree(bt);
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);