[BACK]Return to blktrace.c CVS log [TXT][DIR] Up to [Development] / linux-2.6-xfs / drivers / block

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);